dsh-d365-kit 0.34.1 → 0.34.4

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +319 -308
  2. package/CHANGELOG.zh-CN.md +318 -307
  3. package/INSTALL.md +94 -94
  4. package/INSTALL.zh-CN.md +93 -93
  5. package/LICENSE +21 -21
  6. package/README.md +336 -327
  7. package/README.zh-CN.md +336 -327
  8. package/client.js +1818 -1818
  9. package/cordis.patch.yml +44 -44
  10. package/docs/en/architecture.md +172 -172
  11. package/docs/en/checks.md +184 -184
  12. package/docs/en/installation.md +151 -151
  13. package/docs/en/permissions.md +161 -161
  14. package/docs/en/scenarios.md +202 -202
  15. package/docs/en/tools.md +665 -665
  16. package/docs/en/troubleshooting.md +131 -131
  17. package/docs/zh/architecture.md +172 -172
  18. package/docs/zh/checks.md +184 -184
  19. package/docs/zh/installation.md +151 -151
  20. package/docs/zh/permissions.md +161 -161
  21. package/docs/zh/scenarios.md +202 -202
  22. package/docs/zh/tools.md +665 -665
  23. package/docs/zh/troubleshooting.md +131 -131
  24. package/examples/prompts.en.md +206 -206
  25. package/examples/prompts.zh-CN.md +206 -206
  26. package/examples/waivers.example.json +20 -20
  27. package/icon.svg +6 -6
  28. package/index.js +139 -139
  29. package/lib/auth.js +181 -181
  30. package/lib/client.js +496 -496
  31. package/lib/code-check.js +549 -549
  32. package/lib/config.js +323 -323
  33. package/lib/contract-history.js +49 -49
  34. package/lib/contract.js +583 -583
  35. package/lib/csharp-check.js +493 -493
  36. package/lib/data-migrate.js +1318 -1318
  37. package/lib/data-policy.js +78 -78
  38. package/lib/error-codes.js +80 -80
  39. package/lib/gitlab.js +161 -161
  40. package/lib/health.js +205 -205
  41. package/lib/migrate.js +732 -732
  42. package/lib/preflight.js +116 -116
  43. package/lib/refs.js +191 -191
  44. package/lib/resources.js +320 -320
  45. package/lib/retention-defaults.js +17 -17
  46. package/lib/retention.js +384 -384
  47. package/lib/runtime.js +232 -232
  48. package/lib/settings-http.js +512 -512
  49. package/lib/settings-store.js +371 -371
  50. package/lib/snapshot.js +149 -149
  51. package/lib/tool-code-check.js +133 -133
  52. package/lib/tool-configure.js +169 -169
  53. package/lib/tool-contract.js +376 -376
  54. package/lib/tool-data-sync.js +559 -559
  55. package/lib/tool-data-write.js +203 -203
  56. package/lib/tool-data.js +282 -282
  57. package/lib/tool-diff.js +484 -484
  58. package/lib/tool-environments.js +106 -106
  59. package/lib/tool-flows.js +540 -540
  60. package/lib/tool-health.js +127 -127
  61. package/lib/tool-layers.js +497 -497
  62. package/lib/tool-metadata.js +417 -417
  63. package/lib/tool-migrate.js +102 -102
  64. package/lib/tool-refs.js +206 -206
  65. package/lib/tool-reports.js +299 -299
  66. package/lib/tool-sync.js +430 -430
  67. package/lib/tool-write.js +563 -563
  68. package/lib/waivers.js +83 -83
  69. package/lib/write-actions.js +1374 -1374
  70. package/lib/write-policy.js +199 -199
  71. package/lib/write-store.js +167 -167
  72. package/locale/en.json +68 -68
  73. package/locale/zh.json +68 -68
  74. package/package.json +2 -2
  75. package/skills/d365-troubleshooting/SKILL.md +115 -115
package/README.md CHANGED
@@ -1,327 +1,336 @@
1
- # dsh-d365-kit (D365 Toolkit)
2
-
3
- **English** | [简体中文](README.zh-CN.md)
4
-
5
- A **development and deployment assistant** for Dynamics 365 CE / Dataverse, installed as a [DeepSeek Harness](https://github.com/deepseek-ai) bundle.
6
- It turns a real D365 organization into something an agent can query directly: metadata, solution layers, snapshot diffs, references, migration risk, the impact of code changes, and controlled writes.
7
-
8
- > Nineteen tools, one settings page. Read-only by default: tick a capability and it may do exactly that — even metadata reads are a tick of "Read metadata", on by default.
9
-
10
- ```
11
- You: which forms and views still use the contoso_customertier field on account in DEV?
12
- agent: 17 forms, 64 views (62 columns / 50 filters), 0 business processes …
13
- ```
14
-
15
- ---
16
-
17
- ## What problem it solves
18
-
19
- Most day-to-day friction in D365 projects is not "can't write code", it is **information asymmetry between environments**:
20
-
21
- | Problem | How this plugin answers |
22
- | --- | --- |
23
- | If I ship this solution to SIT, will it override another solution's customizations? | `d365_migrate` checks layers component by component, listing the solutions that will be shadowed, the components that are missing, and the option values that will be lost |
24
- | Why is the same form different in two environments? | `d365_layers` tells you who overrode it, `d365_diff` gives you the field-level differences |
25
- | Will deleting this field break something? | `d365_refs` reverse-looks-up forms / views / processes / plugin steps, `d365_data` shows how many records still use it |
26
- | Will this MR run once deployed to UAT? | `d365_code_check` maps JS to web resources and forms, and C# to assemblies/types/steps/images |
27
- | Upstream changed a plugin — has downstream caught up? | `d365_contract` treats upstream registrations as a contract and compares them item by item downstream, with waivers and trends |
28
- | Can we deploy right now? | `d365_health` shows suspended imports/publishes and failed jobs; the pre-flight check before writes uses the same data |
29
- | Need to add a field / option value / view column? | `d365_write` produces a plan → a human confirms in the conversation → only then does it write, with a queryable audit trail afterwards |
30
-
31
- ---
32
-
33
- ## Feature overview
34
-
35
- ### Connectivity and environments
36
-
37
- | Tool | Purpose |
38
- | --- | --- |
39
- | `d365_envs` | Environment inventory, whether credentials are complete; with `check: true` it actually connects once |
40
- | `d365_configure` | Add, edit or remove environments (URL, tenant, ClientId, secret), test connectivity |
41
- | `d365_health` | Suspended/failed background jobs, in-progress import publishes, recently installed solutions |
42
-
43
- ### Metadata and solution layers
44
-
45
- | Tool | Purpose |
46
- | --- | --- |
47
- | `d365_metadata` | 19 resource kinds: entities, attributes, relationships, forms, views, workflows, business rules, plugins, web resources, solutions, components, Custom APIs, sitemaps, option sets |
48
- | `d365_layers` | `msdyn_componentlayer`: which solutions have overridden a component, who is on top, and which layer is currently active |
49
-
50
- ### Snapshots and diff
51
-
52
- | Tool | Purpose |
53
- | --- | --- |
54
- | `d365_sync` | Pulls an environment's metadata into a local snapshot, recording "complete / truncated / failed" per resource |
55
- | `d365_diff` | Field-level differences between two snapshots (added, removed, modified), with noise filtering and a stale-snapshot warning |
56
-
57
- ### Impact and data
58
-
59
- | Tool | Purpose |
60
- | --- | --- |
61
- | `d365_refs` | Reverse lookup: field → forms/views/processes; web resource → forms and events; assembly → types and steps |
62
- | `d365_data` | Business records: count / group by / sample (requires `data.read`) |
63
- | `d365_data_write` | Create or update one business record, executed after confirmation in the conversation (requires `data.write`) |
64
- | `d365_data_sync` | Mirror records between environments (keeps GUIDs, resolves lookups, reports conflicts), or update one field on a list of records |
65
-
66
- ### Writes
67
-
68
- | Tool | Purpose |
69
- | --- | --- |
70
- | `d365_write` | Add/update/delete option values, add/update/delete fields, rename a table, add or remove system view columns; `plan-*` only produces a plan, `execute` requires manual confirmation |
71
-
72
- ### Migration and checks
73
-
74
- | Tool | Purpose |
75
- | --- | --- |
76
- | `d365_migrate` | Solution migration risk assessment: layer shadowing, missing components, lost option values, version and managed state |
77
- | `d365_contract` | Contract matrix and drift: assemblies, plugin types, steps, form libraries, handlers, sitemaps, option values; with waivers and trends |
78
- | `d365_code_check` | Reads a GitLab MR, maps JS/C# changes onto the target environment and reports blocker / warning / manual confirmation |
79
-
80
- ### Diagnostics
81
-
82
- | Tool | Purpose |
83
- | --- | --- |
84
- | `d365_trace` | Plug-in trace logs: what ran, what threw, grouped by signature; credentials in the message block are masked (requires `data.read`) |
85
- | `d365_invoke` | Call one Dataverse message and see the real status and body; GET functions need `data.read`, POST actions need `message.invoke` plus a confirmation each time |
86
- | `d365_flows` | Power Automate cloud flows (`workflow` rows with `category = 5`): catalog, one flow's trigger/actions/connectors, and which flows touch a given table |
87
-
88
- ### Operations
89
-
90
- | Tool | Purpose |
91
- | --- | --- |
92
- | `d365_reports` | Local artifacts and disk usage, cleanup per retention policy (dry run by default), snapshot compaction |
93
-
94
- ---
95
-
96
- ## Settings page
97
-
98
- "Settings → D365 Toolkit" in the DSH Web UI, six tabs:
99
-
100
- | Tab | Contents |
101
- | --- | --- |
102
- | Environments | Environment inventory, test connection, default environment |
103
- | Permissions | Tick capabilities per environment (read business data / write kinds), default solution for changes, copy to other environments |
104
- | Plans | Pending write plans, recent change records and publish results (re-publishable) |
105
- | Data | File count / size / retention policy per artifact kind, dry run and cleanup, snapshot compaction |
106
- | GitLab | Repository URL, default project, read-only token (`read_api`) |
107
- | General | Snapshots to keep, default row limit, request timeout |
108
-
109
- The UI language follows DSH (Chinese / English). The environment inventory and `d365_configure` go through the same storage code, so what the UI shows and what the agent sees always match.
110
-
111
- ---
112
-
113
- ## Installation
114
-
115
- Prerequisites: DSH desktop; network access to `*.dynamics.com`; a read-only application user (ClientId + secret).
116
-
117
- ```bash
118
- # From npm (shortest, upgradeable; works once the package is published)
119
- plugin_manager install_bundle target="dsh-d365-kit@0.34.1"
120
-
121
- # From Git (no registry needed, pin a tag)
122
- plugin_manager install_bundle target="git+https://<your-git>/<group>/dsh-d365-kit.git#v0.34.1"
123
-
124
- # From a local directory (link: code changes take effect immediately)
125
- plugin_manager install_bundle target="D:\path\to\dsh-d365-kit"
126
- ```
127
-
128
- `dsh-d365-kit@<version>` is resolved from the **npm registry**; a GitHub repository alone is reachable through the `git+https://` form (that is how the second command works today). The repo ships `.github/workflows/publish.yml`, so pushing a `v*` tag publishes to npm once `NPM_TOKEN` is configured.
129
-
130
- After installing, **restart DSH once** so the web client loads the settings page code into its module graph.
131
-
132
- Then: Settings → D365 Toolkit → Environments → Add environment (key, URL, tenant, ClientId, secret) → Test connection → Save.
133
- Read-only by default; to read business records, tick "Read business data" under "Permissions".
134
-
135
- For full steps, credential resolution order, upgrade and uninstall, see [docs/en/installation.md](docs/en/installation.md).
136
-
137
- ---
138
-
139
- ## Typical usage
140
-
141
- ### 1. Assess a solution before deploying
142
-
143
- ```
144
- What are the risks of shipping this solution to SIT and UAT?
145
-
146
- d365_migrate action=assess source=dev solution=ContosoBase targets=["sit","uat"]
147
- ```
148
-
149
- The report answers: does the target already have this solution, is the source version higher, whose layers will sit on top of each component (listing the shadowed solutions), and will option sets lose values. It lands in `.d365/reports/migration-*.md`.
150
-
151
- ### 2. Can this field be deleted
152
-
153
- ```
154
- Who else still uses account.contoso_customertier? Also check how many records carry a value.
155
-
156
- d365_refs kind=field entity=account attribute=contoso_customertier
157
- d365_data action=group entity=account field=contoso_customertier
158
- ```
159
-
160
- ### 3. Why two environments differ
161
-
162
- ```
163
- d365_sync environments=["dev","sit"]
164
- d365_diff base=dev head=sit resources=["forms","views","attributes"]
165
- d365_layers component=Account componentType=SystemForm
166
- ```
167
-
168
- ### 4. Check whether an MR can ship to UAT
169
-
170
- ```
171
- d365_code_check mergeRequest="https://gitlab.example.com/contoso/powerapps/-/merge_requests/7759/diffs" targets=["uat"]
172
- ```
173
-
174
- JS changes are mapped to web resources and forms, checking whether the fields they read are on the form and whether deleted functions are still referenced by a handler;
175
- C# changes are checked against assembly version, plugin types, steps, step images and filtering attributes. Anything that cannot be decided statically goes into the "manual confirmation" list.
176
-
177
- ### 5. Add 36 tax ID option values (the full write flow)
178
-
179
- ```
180
- d365_write action=plan-add-options environment=dev optionSet=contoso_taxcategoryos labels=["AD3|Andorra: Tax Register Number", ...]
181
-
182
- (the agent returns the plan and the operation list)
183
- Run mw04aul6-1ci6aa
184
- ```
185
-
186
- The second step pops a confirmation card in the conversation; nothing is written until you click "Approve and execute". After writing, only the affected option set is published — the whole organization is not recalculated.
187
-
188
- ### 6. Upstream plugins changed — what is downstream missing
189
-
190
- ```
191
- d365_contract action=drift source=dev targets=["sit","uat"] forms=true
192
- ```
193
-
194
- For more phrasings you can copy straight into a conversation, see [examples/prompts.en.md](examples/prompts.en.md) and [docs/en/scenarios.md](docs/en/scenarios.md).
195
-
196
- ---
197
-
198
- ## Permission model
199
-
200
- By default **read-only**. Capabilities are granted per environment and live in `write.environments` of `d365.config.json`; the settings page renders one block per environment, separated by a rule:
201
-
202
- ```json
203
- {
204
- "write": {
205
- "capabilityVersion": 2,
206
- "environments": {
207
- "dev": ["metadata.read", "data.read", "optionValue.add", "attribute.add"],
208
- "sit": ["metadata.read", "data.read"]
209
- },
210
- "solutionUniqueName": "ContosoBase"
211
- }
212
- }
213
- ```
214
-
215
- An environment with no entry has never been configured and gets only `metadata.read`; an empty array revokes everything.
216
-
217
- | Capability | Operation | Effect |
218
- | --- | --- | --- |
219
- | `metadata.read` | metadata `GET` | Read tables / fields / forms / views / plugins / layers (on by default) |
220
- | `data.read` | business-data `GET` | Read business records; without it the records in tables are unreadable |
221
- | `data.write` | `POST` / `PATCH` | Create or update business records (no delete) |
222
- | `optionValue.add` | `InsertOptionValue` | Add an option value (global or attribute-local) |
223
- | `optionValue.updateLabel` | `UpdateOptionValue` | Change an option label |
224
- | `optionValue.delete` | `DeleteOptionValue` | Permanently delete an option value (high risk) |
225
- | `attribute.add` | `CreateAttribute` | Add a field |
226
- | `attribute.update` | `UpdateAttribute` | Change a field's display name / description / required level |
227
- | `attribute.delete` | `DeleteAttribute` | Permanently delete a field and its data (high risk) |
228
- | `entity.update` | `UpdateEntity` | Change a table's display name and description |
229
- | `view.columns` | `UpdateSavedQuery` | Add or remove system view columns |
230
- | `layer.remove` | `RemoveActiveCustomizations` | Remove a component's top-most unmanaged layer (high risk) |
231
-
232
- Writes take two steps: `plan-*` generates a plan read-only → a human confirms in the conversation (or the settings page) → `execute`. Before execution it checks whether the environment is importing/publishing and refuses if it is busy; every execution is recorded item by item in `.d365/changes/`, plans move through `pending → running → done/failed`, and a re-run does not submit twice.
233
-
234
- The decision lives in one place, `lib/client.js`: metadata requests need `metadata.read`, every other `GET` is blocked as business data unless `data.read` is granted, and writes additionally need `data.write`. All of it happens before the token is fetched, so a new tool cannot bypass these gates.
235
-
236
- See [docs/en/permissions.md](docs/en/permissions.md) for details.
237
-
238
- ---
239
-
240
- ## Local artifacts
241
-
242
- ```
243
- <workspace>/
244
- d365.config.json # environment inventory and tool parameters (can be committed)
245
- d365.secrets.json # client secret (plaintext — do not commit)
246
- .d365/
247
- snapshots/<env>/<id>/ # metadata snapshots + manifest
248
- diffs/ # diff reports
249
- reports/ # migration, contract, code check, historical trends
250
- query/ # query evidence, data write audit
251
- pending-writes/ # pending plans
252
- changes/ # executed changes and publish results
253
- waivers.json # contract waivers
254
- ```
255
-
256
- `.d365/` ships its own `.gitignore` (contents `*`). Default retention: snapshots 2 files/30 days, reports 50/90, diff 20/30, query 100/7, changes 200/180; cleanup runs automatically after artifacts are written, and `waivers.json` plus pending plans are never cleaned up.
257
-
258
- ---
259
-
260
- ## Documentation
261
-
262
- | Document | Contents |
263
- | --- | --- |
264
- | [docs/en/installation.md](docs/en/installation.md) | Installation, first-time setup, credentials, upgrade, uninstall |
265
- | [docs/en/tools.md](docs/en/tools.md) | Parameters, return values and examples for the 19 tools |
266
- | [docs/en/scenarios.md](docs/en/scenarios.md) | Complete walkthroughs for 8 real scenarios |
267
- | [docs/en/permissions.md](docs/en/permissions.md) | Capability table, write flow, pre-flight check, safety boundary |
268
- | [docs/en/checks.md](docs/en/checks.md) | Rules and confidence levels for migration assessment, contract drift and code checks |
269
- | [docs/en/troubleshooting.md](docs/en/troubleshooting.md) | Common error codes and troubleshooting |
270
- | [skills/d365-troubleshooting/SKILL.md](skills/d365-troubleshooting/SKILL.md) | Agent skill: how to work a plug-in failure from trace to reproduction (registered by the plugin, no copy needed) |
271
- | [docs/en/architecture.md](docs/en/architecture.md) | Module layout, data flow, how to add a tool |
272
- | [CHANGELOG.md](CHANGELOG.md) | Version history |
273
-
274
- ---
275
-
276
- ## Known limitations
277
-
278
- - Does not collect Ribbon or Power Automate cloud flows.
279
- - `attributes` is requested per entity, so with many entities the request count equals the entity count; use `entities` to narrow the scope.
280
- - Metadata tables mostly have no incremental cursor, so every sync is a full pull; a delta is only possible by "running it a second time, then diffing".
281
- - `d365_layers` relies on the `msdyn_componentlayer` virtual table: you must supply both the component id and the type; a name alone returns 0 rows.
282
- - Component types 36 / 50 / 70 (report and chart kinds) have no matching name in the layer table, so only their existence is checked.
283
- - Layer order is determined by **import time**, not version number: whoever imported later is on top.
284
- - `secureconfiguration` always reads back empty through the Web API, so plugin secure config can only be confirmed manually.
285
- - Contract drift reads live data (not snapshots) and takes tens of seconds to a few minutes on a large environment.
286
-
287
- ---
288
-
289
- ## Development
290
-
291
- ```
292
- index.js # Host entry: registers the 19 tools, mounts the settings page routes
293
- client.js # settings page (six tabs, no build step)
294
- lib/
295
- client.js # Dataverse Web API client (retry, paging, permission gate)
296
- auth.js # Entra ID client credentials flow + token cache
297
- config.js # config loading and normalization
298
- resources.js # query definitions for the 19 metadata resource kinds
299
- write-actions.js # planner / executor for all write operations
300
- contract.js # contract matrix and drift rules
301
- migrate.js # migration assessment
302
- code-check.js # MR check (JS)
303
- csharp-check.js # MR check (C#)
304
- refs.js # reverse index
305
- retention.js # retention policy and disk cleanup
306
- ...
307
- ```
308
-
309
- Zero runtime dependencies: only Node's built-in `fetch` / `fs` / `path`. After changes, validate each file with `node --check`; host-side changes take effect only after reloading the plugin (`plugin_manager set_plugin enabled=false` then `true`), client code needs a page refresh.
310
-
311
- Conventions for a new tool: `parameters` and `output.schema` both use standard JSON Schema; return "summary + file path" and write the body to a file; after registering, add it to the disposers in `index.js`.
312
-
313
- ---
314
-
315
- ## Status
316
-
317
- An internal tool, evolving with project needs. Version semantics: bug fixes bump patch, new tools/capabilities bump minor, config structure changes bump major.
318
-
319
- Environment configuration and secrets are not shipped with the package; each machine fills in `d365.config.json` and `d365.secrets.json` itself.
320
-
321
- ---
322
-
323
- ## License
324
-
325
- [MIT](LICENSE) © 2026 iTanzhao
326
-
327
- Use it, change it, ship it — keep the copyright notice. No warranty, no trademark rights.
1
+ # dsh-d365-kit (D365 Toolkit)
2
+
3
+ **English** | [简体中文](README.zh-CN.md)
4
+
5
+ A **development and deployment assistant** for Dynamics 365 CE / Dataverse, installed as a [DeepSeek Harness](https://github.com/deepseek-ai) bundle.
6
+ It turns a real D365 organization into something an agent can query directly: metadata, solution layers, snapshot diffs, references, migration risk, the impact of code changes, and controlled writes.
7
+
8
+ > Nineteen tools, one settings page. Read-only by default: tick a capability and it may do exactly that — even metadata reads are a tick of "Read metadata", on by default.
9
+
10
+ ```
11
+ You: which forms and views still use the contoso_customertier field on account in DEV?
12
+ agent: 17 forms, 64 views (62 columns / 50 filters), 0 business processes …
13
+ ```
14
+
15
+ ---
16
+
17
+ ## What problem it solves
18
+
19
+ Most day-to-day friction in D365 projects is not "can't write code", it is **information asymmetry between environments**:
20
+
21
+ | Problem | How this plugin answers |
22
+ | --- | --- |
23
+ | If I ship this solution to SIT, will it override another solution's customizations? | `d365_migrate` checks layers component by component, listing the solutions that will be shadowed, the components that are missing, and the option values that will be lost |
24
+ | Why is the same form different in two environments? | `d365_layers` tells you who overrode it, `d365_diff` gives you the field-level differences |
25
+ | Will deleting this field break something? | `d365_refs` reverse-looks-up forms / views / processes / plugin steps, `d365_data` shows how many records still use it |
26
+ | Will this MR run once deployed to UAT? | `d365_code_check` maps JS to web resources and forms, and C# to assemblies/types/steps/images |
27
+ | Upstream changed a plugin — has downstream caught up? | `d365_contract` treats upstream registrations as a contract and compares them item by item downstream, with waivers and trends |
28
+ | Can we deploy right now? | `d365_health` shows suspended imports/publishes and failed jobs; the pre-flight check before writes uses the same data |
29
+ | Need to add a field / option value / view column? | `d365_write` produces a plan → a human confirms in the conversation → only then does it write, with a queryable audit trail afterwards |
30
+
31
+ ---
32
+
33
+ ## Feature overview
34
+
35
+ ### Connectivity and environments
36
+
37
+ | Tool | Purpose |
38
+ | --- | --- |
39
+ | `d365_envs` | Environment inventory, whether credentials are complete; with `check: true` it actually connects once |
40
+ | `d365_configure` | Add, edit or remove environments (URL, tenant, ClientId, secret), test connectivity |
41
+ | `d365_health` | Suspended/failed background jobs, in-progress import publishes, recently installed solutions |
42
+
43
+ ### Metadata and solution layers
44
+
45
+ | Tool | Purpose |
46
+ | --- | --- |
47
+ | `d365_metadata` | 19 resource kinds: entities, attributes, relationships, forms, views, workflows, business rules, plugins, web resources, solutions, components, Custom APIs, sitemaps, option sets |
48
+ | `d365_layers` | `msdyn_componentlayer`: which solutions have overridden a component, who is on top, and which layer is currently active |
49
+
50
+ ### Snapshots and diff
51
+
52
+ | Tool | Purpose |
53
+ | --- | --- |
54
+ | `d365_sync` | Pulls an environment's metadata into a local snapshot, recording "complete / truncated / failed" per resource |
55
+ | `d365_diff` | Field-level differences between two snapshots (added, removed, modified), with noise filtering and a stale-snapshot warning |
56
+
57
+ ### Impact and data
58
+
59
+ | Tool | Purpose |
60
+ | --- | --- |
61
+ | `d365_refs` | Reverse lookup: field → forms/views/processes; web resource → forms and events; assembly → types and steps |
62
+ | `d365_data` | Business records: count / group by / sample (requires `data.read`) |
63
+ | `d365_data_write` | Create or update one business record, executed after confirmation in the conversation (requires `data.write`) |
64
+ | `d365_data_sync` | Mirror records between environments (keeps GUIDs, resolves lookups, reports conflicts), or update one field on a list of records |
65
+
66
+ ### Writes
67
+
68
+ | Tool | Purpose |
69
+ | --- | --- |
70
+ | `d365_write` | Add/update/delete option values, add/update/delete fields, rename a table, add or remove system view columns; `plan-*` only produces a plan, `execute` requires manual confirmation |
71
+
72
+ ### Migration and checks
73
+
74
+ | Tool | Purpose |
75
+ | --- | --- |
76
+ | `d365_migrate` | Solution migration risk assessment: layer shadowing, missing components, lost option values, version and managed state |
77
+ | `d365_contract` | Contract matrix and drift: assemblies, plugin types, steps, form libraries, handlers, sitemaps, option values; with waivers and trends |
78
+ | `d365_code_check` | Reads a GitLab MR, maps JS/C# changes onto the target environment and reports blocker / warning / manual confirmation |
79
+
80
+ ### Diagnostics
81
+
82
+ | Tool | Purpose |
83
+ | --- | --- |
84
+ | `d365_trace` | Plug-in trace logs: what ran, what threw, grouped by signature; credentials in the message block are masked (requires `data.read`) |
85
+ | `d365_invoke` | Call one Dataverse message and see the real status and body; GET functions need `data.read`, POST actions need `message.invoke` plus a confirmation each time |
86
+ | `d365_flows` | Power Automate cloud flows (`workflow` rows with `category = 5`): catalog, one flow's trigger/actions/connectors, and which flows touch a given table |
87
+
88
+ ### Operations
89
+
90
+ | Tool | Purpose |
91
+ | --- | --- |
92
+ | `d365_reports` | Local artifacts and disk usage, cleanup per retention policy (dry run by default), snapshot compaction |
93
+
94
+ ---
95
+
96
+ ## Settings page
97
+
98
+ "Settings → D365 Toolkit" in the DSH Web UI, six tabs:
99
+
100
+ | Tab | Contents |
101
+ | --- | --- |
102
+ | Environments | Environment inventory, test connection, default environment |
103
+ | Permissions | Tick capabilities per environment (read business data / write kinds), default solution for changes, copy to other environments |
104
+ | Plans | Pending write plans, recent change records and publish results (re-publishable) |
105
+ | Data | File count / size / retention policy per artifact kind, dry run and cleanup, snapshot compaction |
106
+ | GitLab | Repository URL, default project, read-only token (`read_api`) |
107
+ | General | Snapshots to keep, default row limit, request timeout |
108
+
109
+ The UI language follows DSH (Chinese / English). The environment inventory and `d365_configure` go through the same storage code, so what the UI shows and what the agent sees always match.
110
+
111
+ ---
112
+
113
+ ## Installation
114
+
115
+ Prerequisites: DSH desktop; network access to `*.dynamics.com`; a read-only application user (ClientId + secret).
116
+
117
+ ```bash
118
+ # From npm (shortest, upgradeable; works once the package is published)
119
+ plugin_manager install_bundle target="dsh-d365-kit@0.34.4"
120
+
121
+ # From Git (no registry needed, pin a tag)
122
+ plugin_manager install_bundle target="git+https://<your-git>/<group>/dsh-d365-kit.git#v0.34.4"
123
+
124
+ # From a local directory (link: code changes take effect immediately)
125
+ plugin_manager install_bundle target="D:\path\to\dsh-d365-kit"
126
+ ```
127
+
128
+ `dsh-d365-kit@<version>` is resolved from the **npm registry**; a GitHub repository alone is reachable through the `git+https://` form (that is how the second command works today). `.github/workflows/publish.yml` publishes to npm **on every push to `main` whose `package.json` version is not on the registry yet** — bump the version, push, and npm gets it; push without a bump and the job exits green without doing anything. A `v*` tag is optional and only acts as a consistency check (it must equal the version in `package.json`).
129
+
130
+ Publishing needs one of two credentials, and the workflow stays a no-op until one exists:
131
+
132
+ | Auth | What to configure | Secret on disk |
133
+ | --- | --- | --- |
134
+ | Trusted publishing (recommended) | repo variable `NPM_TRUSTED_PUBLISHING = true`, plus a trusted publisher on npmjs.com pointing at this repo and workflow file | none |
135
+ | Granular token | repo secret `NPM_TOKEN` (needs "Bypass two-factor authentication" to publish) | a token you must rotate |
136
+
137
+ Trusted publishing is the one to prefer: the token path is exactly the thing that leaks into logs and chat transcripts.
138
+
139
+ After installing, **restart DSH once** so the web client loads the settings page code into its module graph.
140
+
141
+ Then: Settings → D365 Toolkit → Environments → Add environment (key, URL, tenant, ClientId, secret) → Test connection → Save.
142
+ Read-only by default; to read business records, tick "Read business data" under "Permissions".
143
+
144
+ For full steps, credential resolution order, upgrade and uninstall, see [docs/en/installation.md](docs/en/installation.md).
145
+
146
+ ---
147
+
148
+ ## Typical usage
149
+
150
+ ### 1. Assess a solution before deploying
151
+
152
+ ```
153
+ What are the risks of shipping this solution to SIT and UAT?
154
+
155
+ d365_migrate action=assess source=dev solution=ContosoBase targets=["sit","uat"]
156
+ ```
157
+
158
+ The report answers: does the target already have this solution, is the source version higher, whose layers will sit on top of each component (listing the shadowed solutions), and will option sets lose values. It lands in `.d365/reports/migration-*.md`.
159
+
160
+ ### 2. Can this field be deleted
161
+
162
+ ```
163
+ Who else still uses account.contoso_customertier? Also check how many records carry a value.
164
+
165
+ d365_refs kind=field entity=account attribute=contoso_customertier
166
+ d365_data action=group entity=account field=contoso_customertier
167
+ ```
168
+
169
+ ### 3. Why two environments differ
170
+
171
+ ```
172
+ d365_sync environments=["dev","sit"]
173
+ d365_diff base=dev head=sit resources=["forms","views","attributes"]
174
+ d365_layers component=Account componentType=SystemForm
175
+ ```
176
+
177
+ ### 4. Check whether an MR can ship to UAT
178
+
179
+ ```
180
+ d365_code_check mergeRequest="https://gitlab.example.com/contoso/powerapps/-/merge_requests/7759/diffs" targets=["uat"]
181
+ ```
182
+
183
+ JS changes are mapped to web resources and forms, checking whether the fields they read are on the form and whether deleted functions are still referenced by a handler;
184
+ C# changes are checked against assembly version, plugin types, steps, step images and filtering attributes. Anything that cannot be decided statically goes into the "manual confirmation" list.
185
+
186
+ ### 5. Add 36 tax ID option values (the full write flow)
187
+
188
+ ```
189
+ d365_write action=plan-add-options environment=dev optionSet=contoso_taxcategoryos labels=["AD3|Andorra: Tax Register Number", ...]
190
+
191
+ (the agent returns the plan and the operation list)
192
+ Run mw04aul6-1ci6aa
193
+ ```
194
+
195
+ The second step pops a confirmation card in the conversation; nothing is written until you click "Approve and execute". After writing, only the affected option set is published — the whole organization is not recalculated.
196
+
197
+ ### 6. Upstream plugins changed — what is downstream missing
198
+
199
+ ```
200
+ d365_contract action=drift source=dev targets=["sit","uat"] forms=true
201
+ ```
202
+
203
+ For more phrasings you can copy straight into a conversation, see [examples/prompts.en.md](examples/prompts.en.md) and [docs/en/scenarios.md](docs/en/scenarios.md).
204
+
205
+ ---
206
+
207
+ ## Permission model
208
+
209
+ By default **read-only**. Capabilities are granted per environment and live in `write.environments` of `d365.config.json`; the settings page renders one block per environment, separated by a rule:
210
+
211
+ ```json
212
+ {
213
+ "write": {
214
+ "capabilityVersion": 2,
215
+ "environments": {
216
+ "dev": ["metadata.read", "data.read", "optionValue.add", "attribute.add"],
217
+ "sit": ["metadata.read", "data.read"]
218
+ },
219
+ "solutionUniqueName": "ContosoBase"
220
+ }
221
+ }
222
+ ```
223
+
224
+ An environment with no entry has never been configured and gets only `metadata.read`; an empty array revokes everything.
225
+
226
+ | Capability | Operation | Effect |
227
+ | --- | --- | --- |
228
+ | `metadata.read` | metadata `GET` | Read tables / fields / forms / views / plugins / layers (on by default) |
229
+ | `data.read` | business-data `GET` | Read business records; without it the records in tables are unreadable |
230
+ | `data.write` | `POST` / `PATCH` | Create or update business records (no delete) |
231
+ | `optionValue.add` | `InsertOptionValue` | Add an option value (global or attribute-local) |
232
+ | `optionValue.updateLabel` | `UpdateOptionValue` | Change an option label |
233
+ | `optionValue.delete` | `DeleteOptionValue` | Permanently delete an option value (high risk) |
234
+ | `attribute.add` | `CreateAttribute` | Add a field |
235
+ | `attribute.update` | `UpdateAttribute` | Change a field's display name / description / required level |
236
+ | `attribute.delete` | `DeleteAttribute` | Permanently delete a field and its data (high risk) |
237
+ | `entity.update` | `UpdateEntity` | Change a table's display name and description |
238
+ | `view.columns` | `UpdateSavedQuery` | Add or remove system view columns |
239
+ | `layer.remove` | `RemoveActiveCustomizations` | Remove a component's top-most unmanaged layer (high risk) |
240
+
241
+ Writes take two steps: `plan-*` generates a plan read-only → a human confirms in the conversation (or the settings page) → `execute`. Before execution it checks whether the environment is importing/publishing and refuses if it is busy; every execution is recorded item by item in `.d365/changes/`, plans move through `pending → running → done/failed`, and a re-run does not submit twice.
242
+
243
+ The decision lives in one place, `lib/client.js`: metadata requests need `metadata.read`, every other `GET` is blocked as business data unless `data.read` is granted, and writes additionally need `data.write`. All of it happens before the token is fetched, so a new tool cannot bypass these gates.
244
+
245
+ See [docs/en/permissions.md](docs/en/permissions.md) for details.
246
+
247
+ ---
248
+
249
+ ## Local artifacts
250
+
251
+ ```
252
+ <workspace>/
253
+ d365.config.json # environment inventory and tool parameters (can be committed)
254
+ d365.secrets.json # client secret (plaintext — do not commit)
255
+ .d365/
256
+ snapshots/<env>/<id>/ # metadata snapshots + manifest
257
+ diffs/ # diff reports
258
+ reports/ # migration, contract, code check, historical trends
259
+ query/ # query evidence, data write audit
260
+ pending-writes/ # pending plans
261
+ changes/ # executed changes and publish results
262
+ waivers.json # contract waivers
263
+ ```
264
+
265
+ `.d365/` ships its own `.gitignore` (contents `*`). Default retention: snapshots 2 files/30 days, reports 50/90, diff 20/30, query 100/7, changes 200/180; cleanup runs automatically after artifacts are written, and `waivers.json` plus pending plans are never cleaned up.
266
+
267
+ ---
268
+
269
+ ## Documentation
270
+
271
+ | Document | Contents |
272
+ | --- | --- |
273
+ | [docs/en/installation.md](docs/en/installation.md) | Installation, first-time setup, credentials, upgrade, uninstall |
274
+ | [docs/en/tools.md](docs/en/tools.md) | Parameters, return values and examples for the 19 tools |
275
+ | [docs/en/scenarios.md](docs/en/scenarios.md) | Complete walkthroughs for 8 real scenarios |
276
+ | [docs/en/permissions.md](docs/en/permissions.md) | Capability table, write flow, pre-flight check, safety boundary |
277
+ | [docs/en/checks.md](docs/en/checks.md) | Rules and confidence levels for migration assessment, contract drift and code checks |
278
+ | [docs/en/troubleshooting.md](docs/en/troubleshooting.md) | Common error codes and troubleshooting |
279
+ | [skills/d365-troubleshooting/SKILL.md](skills/d365-troubleshooting/SKILL.md) | Agent skill: how to work a plug-in failure from trace to reproduction (registered by the plugin, no copy needed) |
280
+ | [docs/en/architecture.md](docs/en/architecture.md) | Module layout, data flow, how to add a tool |
281
+ | [CHANGELOG.md](CHANGELOG.md) | Version history |
282
+
283
+ ---
284
+
285
+ ## Known limitations
286
+
287
+ - Does not collect Ribbon or Power Automate cloud flows.
288
+ - `attributes` is requested per entity, so with many entities the request count equals the entity count; use `entities` to narrow the scope.
289
+ - Metadata tables mostly have no incremental cursor, so every sync is a full pull; a delta is only possible by "running it a second time, then diffing".
290
+ - `d365_layers` relies on the `msdyn_componentlayer` virtual table: you must supply both the component id and the type; a name alone returns 0 rows.
291
+ - Component types 36 / 50 / 70 (report and chart kinds) have no matching name in the layer table, so only their existence is checked.
292
+ - Layer order is determined by **import time**, not version number: whoever imported later is on top.
293
+ - `secureconfiguration` always reads back empty through the Web API, so plugin secure config can only be confirmed manually.
294
+ - Contract drift reads live data (not snapshots) and takes tens of seconds to a few minutes on a large environment.
295
+
296
+ ---
297
+
298
+ ## Development
299
+
300
+ ```
301
+ index.js # Host entry: registers the 19 tools, mounts the settings page routes
302
+ client.js # settings page (six tabs, no build step)
303
+ lib/
304
+ client.js # Dataverse Web API client (retry, paging, permission gate)
305
+ auth.js # Entra ID client credentials flow + token cache
306
+ config.js # config loading and normalization
307
+ resources.js # query definitions for the 19 metadata resource kinds
308
+ write-actions.js # planner / executor for all write operations
309
+ contract.js # contract matrix and drift rules
310
+ migrate.js # migration assessment
311
+ code-check.js # MR check (JS)
312
+ csharp-check.js # MR check (C#)
313
+ refs.js # reverse index
314
+ retention.js # retention policy and disk cleanup
315
+ ...
316
+ ```
317
+
318
+ Zero runtime dependencies: only Node's built-in `fetch` / `fs` / `path`. After changes, validate each file with `node --check`; host-side changes take effect only after reloading the plugin (`plugin_manager set_plugin enabled=false` then `true`), client code needs a page refresh.
319
+
320
+ Conventions for a new tool: `parameters` and `output.schema` both use standard JSON Schema; return "summary + file path" and write the body to a file; after registering, add it to the disposers in `index.js`.
321
+
322
+ ---
323
+
324
+ ## Status
325
+
326
+ An internal tool, evolving with project needs. Version semantics: bug fixes bump patch, new tools/capabilities bump minor, config structure changes bump major.
327
+
328
+ Environment configuration and secrets are not shipped with the package; each machine fills in `d365.config.json` and `d365.secrets.json` itself.
329
+
330
+ ---
331
+
332
+ ## License
333
+
334
+ [MIT](LICENSE) © 2026 iTanzhao
335
+
336
+ Use it, change it, ship it — keep the copyright notice. No warranty, no trademark rights.