devworkspace 0.1.1 → 0.2.1

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 (123) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +15 -2
  3. package/dist/agents/claude/content.js +3 -3
  4. package/dist/agents/claude/content.js.map +1 -1
  5. package/dist/cli/args.d.ts +19 -0
  6. package/dist/cli/args.js +6 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/help.js +40 -5
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/render.d.ts +7 -0
  11. package/dist/cli/render.js +104 -17
  12. package/dist/cli/render.js.map +1 -1
  13. package/dist/cli/run.d.ts +6 -0
  14. package/dist/cli/run.js +65 -3
  15. package/dist/cli/run.js.map +1 -1
  16. package/dist/config/schemas.d.ts +7 -1
  17. package/dist/config/schemas.js +10 -0
  18. package/dist/config/schemas.js.map +1 -1
  19. package/dist/core/workspace.js +2 -1
  20. package/dist/core/workspace.js.map +1 -1
  21. package/dist/discovery/commands.d.ts +34 -3
  22. package/dist/discovery/commands.js +95 -33
  23. package/dist/discovery/commands.js.map +1 -1
  24. package/dist/discovery/detect.d.ts +17 -0
  25. package/dist/discovery/detect.js +101 -42
  26. package/dist/discovery/detect.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/integrations/adapters/agent-skills.d.ts +8 -0
  31. package/dist/integrations/adapters/agent-skills.js +25 -0
  32. package/dist/integrations/adapters/agent-skills.js.map +1 -0
  33. package/dist/integrations/adapters/claudePlugin.d.ts +41 -0
  34. package/dist/integrations/adapters/claudePlugin.js +129 -0
  35. package/dist/integrations/adapters/claudePlugin.js.map +1 -0
  36. package/dist/integrations/adapters/graphify.d.ts +2 -0
  37. package/dist/integrations/adapters/graphify.js +277 -0
  38. package/dist/integrations/adapters/graphify.js.map +1 -0
  39. package/dist/integrations/adapters/omniroute.d.ts +6 -0
  40. package/dist/integrations/adapters/omniroute.js +32 -0
  41. package/dist/integrations/adapters/omniroute.js.map +1 -0
  42. package/dist/integrations/adapters/pluginSkill.d.ts +28 -0
  43. package/dist/integrations/adapters/pluginSkill.js +195 -0
  44. package/dist/integrations/adapters/pluginSkill.js.map +1 -0
  45. package/dist/integrations/adapters/ponytail.d.ts +8 -0
  46. package/dist/integrations/adapters/ponytail.js +33 -0
  47. package/dist/integrations/adapters/ponytail.js.map +1 -0
  48. package/dist/integrations/exec.d.ts +26 -0
  49. package/dist/integrations/exec.js +167 -0
  50. package/dist/integrations/exec.js.map +1 -0
  51. package/dist/integrations/index.d.ts +5 -6
  52. package/dist/integrations/index.js +3 -29
  53. package/dist/integrations/index.js.map +1 -1
  54. package/dist/integrations/manager.d.ts +62 -0
  55. package/dist/integrations/manager.js +261 -0
  56. package/dist/integrations/manager.js.map +1 -0
  57. package/dist/integrations/notices.d.ts +13 -0
  58. package/dist/integrations/notices.js +68 -0
  59. package/dist/integrations/notices.js.map +1 -0
  60. package/dist/integrations/persistentPath.d.ts +20 -0
  61. package/dist/integrations/persistentPath.js +47 -0
  62. package/dist/integrations/persistentPath.js.map +1 -0
  63. package/dist/integrations/protect.d.ts +20 -0
  64. package/dist/integrations/protect.js +131 -0
  65. package/dist/integrations/protect.js.map +1 -0
  66. package/dist/integrations/state.d.ts +10 -0
  67. package/dist/integrations/state.js +81 -0
  68. package/dist/integrations/state.js.map +1 -0
  69. package/dist/integrations/types.d.ts +139 -16
  70. package/dist/integrations/types.js +13 -1
  71. package/dist/integrations/types.js.map +1 -1
  72. package/dist/prompts/onboarding.d.ts +3 -3
  73. package/dist/prompts/onboarding.js +9 -8
  74. package/dist/prompts/onboarding.js.map +1 -1
  75. package/dist/services/analysis.d.ts +15 -3
  76. package/dist/services/analysis.js +21 -23
  77. package/dist/services/analysis.js.map +1 -1
  78. package/dist/services/common.d.ts +8 -5
  79. package/dist/services/common.js +2 -5
  80. package/dist/services/common.js.map +1 -1
  81. package/dist/services/doctor.d.ts +10 -0
  82. package/dist/services/doctor.js +87 -19
  83. package/dist/services/doctor.js.map +1 -1
  84. package/dist/services/init.d.ts +9 -2
  85. package/dist/services/init.js +10 -7
  86. package/dist/services/init.js.map +1 -1
  87. package/dist/services/integrations.d.ts +26 -0
  88. package/dist/services/integrations.js +35 -0
  89. package/dist/services/integrations.js.map +1 -0
  90. package/dist/services/refresh.js +1 -1
  91. package/dist/services/refresh.js.map +1 -1
  92. package/dist/services/status.d.ts +4 -2
  93. package/dist/services/status.js +4 -4
  94. package/dist/services/status.js.map +1 -1
  95. package/dist/utils/lock.d.ts +2 -0
  96. package/dist/utils/lock.js +2 -2
  97. package/dist/utils/lock.js.map +1 -1
  98. package/dist/utils/proc.d.ts +8 -1
  99. package/dist/utils/proc.js +31 -8
  100. package/dist/utils/proc.js.map +1 -1
  101. package/docs/architecture.md +1 -1
  102. package/docs/claude.md +4 -0
  103. package/docs/getting-started.md +2 -2
  104. package/docs/integrations.md +98 -33
  105. package/docs/security.md +5 -1
  106. package/docs/testing.md +6 -0
  107. package/docs/troubleshooting.md +11 -1
  108. package/package.json +1 -1
  109. package/dist/integrations/agent-skills.d.ts +0 -10
  110. package/dist/integrations/agent-skills.js +0 -45
  111. package/dist/integrations/agent-skills.js.map +0 -1
  112. package/dist/integrations/graphify.d.ts +0 -7
  113. package/dist/integrations/graphify.js +0 -54
  114. package/dist/integrations/graphify.js.map +0 -1
  115. package/dist/integrations/omniroute.d.ts +0 -7
  116. package/dist/integrations/omniroute.js +0 -24
  117. package/dist/integrations/omniroute.js.map +0 -1
  118. package/dist/integrations/ponytail.d.ts +0 -10
  119. package/dist/integrations/ponytail.js +0 -58
  120. package/dist/integrations/ponytail.js.map +0 -1
  121. package/dist/integrations/probe.d.ts +0 -6
  122. package/dist/integrations/probe.js +0 -41
  123. package/dist/integrations/probe.js.map +0 -1
@@ -0,0 +1,62 @@
1
+ import type { Ws } from '../core/workspace.js';
2
+ import { type IntegrationAdapter, type IntegrationId, type IntegrationReport, type PersistedIntegration, type ProgressEvent, type Runner, type SetupOptions, type StatusCode, type StepLevel } from './types.js';
3
+ /** Tool output goes into reports and the state file: strip secrets and machine-specific paths, flatten to one line. */
4
+ export declare function scrub(text: string): string;
5
+ export declare const ADAPTERS: IntegrationAdapter[];
6
+ export interface ManagerEnv {
7
+ root: string;
8
+ home?: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ platform?: NodeJS.Platform;
11
+ runner?: Runner;
12
+ now?: () => Date;
13
+ adapters?: IntegrationAdapter[];
14
+ }
15
+ export interface StepResult {
16
+ id: string;
17
+ description: string;
18
+ command?: string;
19
+ level: StepLevel;
20
+ state: 'ok' | 'failed' | 'not-run';
21
+ detail?: string;
22
+ requiresApproval?: boolean;
23
+ }
24
+ export type OutcomeAction = 'already-verified' | 'awaiting-restart' | 'nothing-to-do' | 'blocked' | 'dry-run' | 'approval-required' | 'installed' | 'failed' | 'not-selected';
25
+ export interface IntegrationOutcome {
26
+ id: IntegrationId;
27
+ name: string;
28
+ action: OutcomeAction;
29
+ before: StatusCode;
30
+ report: IntegrationReport;
31
+ plan: {
32
+ description: string;
33
+ command?: string;
34
+ level: StepLevel;
35
+ }[];
36
+ steps: StepResult[];
37
+ notes: string[];
38
+ }
39
+ export interface SetupResult {
40
+ dryRun: boolean;
41
+ scope: SetupOptions['scope'];
42
+ outcomes: IntegrationOutcome[];
43
+ events: ProgressEvent[];
44
+ /** Every requested required integration is VERIFIED. */
45
+ complete: boolean;
46
+ approvalRequired: boolean;
47
+ }
48
+ /** Combine what was observed with what earlier runs recorded. Records can only add warnings or RESTART_REQUIRED, never VERIFIED. */
49
+ export declare function applyPersisted(report: IntegrationReport, entry: PersistedIntegration | undefined): IntegrationReport;
50
+ export declare function inspectAll(ws: Ws, mEnv: ManagerEnv, opts?: {
51
+ deep: boolean;
52
+ onlyRequired?: boolean;
53
+ }): Promise<IntegrationReport[]>;
54
+ /** Summary used by doctor/status: required integrations only; OmniRoute never counts. */
55
+ export declare function summarize(reports: IntegrationReport[]): {
56
+ complete: boolean;
57
+ required: number;
58
+ verified: number;
59
+ pending: IntegrationReport[];
60
+ };
61
+ export declare function acknowledgeRestart(ws: Ws, ids: IntegrationId[], now: string): Promise<void>;
62
+ export declare function runSetup(ws: Ws, mEnv: ManagerEnv, setup: SetupOptions, onEvent?: (e: ProgressEvent) => void): Promise<SetupResult>;
@@ -0,0 +1,261 @@
1
+ import os from 'node:os';
2
+ import { onCleanup } from '../utils/cleanup.js';
3
+ import { withLock } from '../utils/lock.js';
4
+ import { redact } from '../security/redact.js';
5
+ import { findAbsolutePaths } from '../utils/paths.js';
6
+ import { agentSkills } from './adapters/agent-skills.js';
7
+ import { graphify } from './adapters/graphify.js';
8
+ import { omniroute } from './adapters/omniroute.js';
9
+ import { ponytail } from './adapters/ponytail.js';
10
+ import { createRunner } from './exec.js';
11
+ import { readPersistentPath } from './persistentPath.js';
12
+ import { restoreSnapshot, takeSnapshot, verifySnapshot } from './protect.js';
13
+ import { blankEntry, loadIntegrationsState, saveIntegrationEntry, wasInterrupted } from './state.js';
14
+ import { REQUIRED_INTEGRATIONS, } from './types.js';
15
+ /** Tool output goes into reports and the state file: strip secrets and machine-specific paths, flatten to one line. */
16
+ export function scrub(text) {
17
+ let out = redact(text);
18
+ for (const p of findAbsolutePaths(out))
19
+ out = out.split(p).join('<path>');
20
+ return out.replace(/\s+/g, ' ').trim().slice(0, 400);
21
+ }
22
+ export const ADAPTERS = [graphify, agentSkills, ponytail, omniroute];
23
+ function resolveEnv(m) {
24
+ const env = m.env ?? process.env;
25
+ const platform = m.platform ?? process.platform;
26
+ return {
27
+ root: m.root,
28
+ home: m.home ?? os.homedir(),
29
+ env,
30
+ platform,
31
+ runner: m.runner ?? createRunner(env, platform),
32
+ now: m.now ?? (() => new Date()),
33
+ adapters: m.adapters ?? ADAPTERS,
34
+ };
35
+ }
36
+ function ctxFor(m, setup, prior, report, signal) {
37
+ let persistent;
38
+ return {
39
+ root: m.root,
40
+ home: m.home,
41
+ env: m.env,
42
+ platform: m.platform,
43
+ runner: m.runner,
44
+ setup,
45
+ signal,
46
+ prior,
47
+ report,
48
+ persistentPath: () => (persistent ??= readPersistentPath(m.runner, m.platform, m.env, signal)),
49
+ };
50
+ }
51
+ const DEFAULT_SETUP = { scope: 'project', yes: false, dryRun: false, repair: false };
52
+ /** Combine what was observed with what earlier runs recorded. Records can only add warnings or RESTART_REQUIRED, never VERIFIED. */
53
+ export function applyPersisted(report, entry) {
54
+ if (!entry)
55
+ return report;
56
+ const out = { ...report, warnings: [...report.warnings], nextSteps: [...report.nextSteps] };
57
+ if (wasInterrupted(entry)) {
58
+ out.warnings.push('A previous setup run was interrupted before it finished.');
59
+ if (!out.nextSteps.includes('devworkspace integrations repair --yes'))
60
+ out.nextSteps.push('devworkspace integrations repair --yes');
61
+ }
62
+ if ((out.status === 'NOT_INSTALLED' || out.status === 'DEGRADED') && entry.lastError) {
63
+ out.status = 'FAILED';
64
+ out.warnings.push(`The last setup attempt failed: ${entry.lastError}`);
65
+ }
66
+ if (entry.restartRequiredAt && (out.status === 'VERIFIED' || out.status === 'INSTALLED')) {
67
+ out.status = 'RESTART_REQUIRED';
68
+ out.nextSteps.push('Restart Claude Code (or run /reload-plugins) so it loads the new skills/plugins/hooks, then run: devworkspace integrations status --restarted');
69
+ }
70
+ return out;
71
+ }
72
+ export async function inspectAll(ws, mEnv, opts = { deep: false }) {
73
+ const m = resolveEnv(mEnv);
74
+ const state = await loadIntegrationsState(ws, m.now().toISOString());
75
+ const list = m.adapters.filter((a) => !opts.onlyRequired || a.required);
76
+ return Promise.all(list.map(async (a) => {
77
+ const ctx = ctxFor(m, DEFAULT_SETUP, state.integrations[a.id], () => undefined);
78
+ try {
79
+ return applyPersisted(await a.inspect(ctx, opts.deep), state.integrations[a.id]);
80
+ }
81
+ catch (e) {
82
+ return {
83
+ id: a.id,
84
+ name: a.name,
85
+ required: a.required,
86
+ status: 'FAILED',
87
+ variant: null,
88
+ scope: null,
89
+ version: null,
90
+ checks: [],
91
+ capabilities: {},
92
+ warnings: [`Inspection failed: ${scrub(e.message)}`],
93
+ nextSteps: ['devworkspace doctor --debug'],
94
+ deep: opts.deep,
95
+ };
96
+ }
97
+ }));
98
+ }
99
+ /** Summary used by doctor/status: required integrations only; OmniRoute never counts. */
100
+ export function summarize(reports) {
101
+ const required = reports.filter((r) => r.required);
102
+ const pending = required.filter((r) => r.status !== 'VERIFIED');
103
+ return { complete: pending.length === 0, required: required.length, verified: required.length - pending.length, pending };
104
+ }
105
+ export async function acknowledgeRestart(ws, ids, now) {
106
+ for (const id of ids)
107
+ await saveIntegrationEntry(ws, id, { restartRequiredAt: null }, now);
108
+ }
109
+ const describeStep = (s) => ({ description: s.description, command: s.command, level: s.level });
110
+ export async function runSetup(ws, mEnv, setup, onEvent = () => undefined) {
111
+ const m = resolveEnv(mEnv);
112
+ const events = [];
113
+ const emit = (e) => {
114
+ events.push(e);
115
+ onEvent(e);
116
+ };
117
+ const ac = new AbortController();
118
+ const disposeCancel = onCleanup(() => ac.abort());
119
+ const wanted = m.adapters.filter((a) => (setup.only ? setup.only.includes(a.id) : REQUIRED_INTEGRATIONS.includes(a.id)));
120
+ const outcomes = [];
121
+ let approvalRequired = false;
122
+ try {
123
+ for (const adapter of wanted) {
124
+ const nowIso = () => m.now().toISOString();
125
+ const state = await loadIntegrationsState(ws, nowIso());
126
+ const prior = state.integrations[adapter.id];
127
+ const ctx = ctxFor(m, setup, prior, emit, ac.signal);
128
+ emit({ id: adapter.id, step: 'inspect', state: 'start', detail: `checking ${adapter.name}` });
129
+ const before = applyPersisted(await adapter.inspect(ctx, true), prior);
130
+ const outcome = (action, report, extra = {}) => ({
131
+ id: adapter.id,
132
+ name: adapter.name,
133
+ action,
134
+ before: before.status,
135
+ report,
136
+ plan: [],
137
+ steps: [],
138
+ notes: [],
139
+ ...extra,
140
+ });
141
+ if (before.status === 'VERIFIED' && !setup.repair) {
142
+ emit({ id: adapter.id, step: 'inspect', state: 'skip', detail: 'already verified' });
143
+ outcomes.push(outcome('already-verified', before));
144
+ continue;
145
+ }
146
+ if (before.status === 'RESTART_REQUIRED' && !setup.repair) {
147
+ emit({ id: adapter.id, step: 'inspect', state: 'skip', detail: 'installed; waiting for a Claude Code restart' });
148
+ outcomes.push(outcome('awaiting-restart', before));
149
+ continue;
150
+ }
151
+ const plan = await adapter.plan(ctx, before);
152
+ if (plan.steps.length === 0) {
153
+ if (plan.blockers.length > 0) {
154
+ emit({ id: adapter.id, step: 'prerequisites', state: 'fail', detail: plan.blockers[0] });
155
+ outcomes.push(outcome('blocked', { ...before, status: 'PREREQUISITES_MISSING', nextSteps: plan.blockers }, { notes: plan.notes }));
156
+ }
157
+ else {
158
+ outcomes.push(outcome('nothing-to-do', before, { notes: plan.notes }));
159
+ }
160
+ continue;
161
+ }
162
+ const planInfo = plan.steps.map(describeStep);
163
+ if (setup.dryRun) {
164
+ outcomes.push(outcome('dry-run', before, { plan: planInfo, notes: plan.notes }));
165
+ continue;
166
+ }
167
+ const needsConsent = plan.steps.some((s) => s.level !== 'project');
168
+ if (needsConsent && !setup.yes) {
169
+ approvalRequired = true;
170
+ const levels = [...new Set(plan.steps.filter((s) => s.level !== 'project').map((s) => s.level))];
171
+ outcomes.push(outcome('approval-required', { ...before, nextSteps: [`Re-run with --yes to approve: ${levels.join(', ')} step(s). Review them first with --dry-run.`, ...before.nextSteps] }, { plan: planInfo, notes: plan.notes }));
172
+ continue;
173
+ }
174
+ // ---- execute ----
175
+ const results = plan.steps.map((s) => ({ id: s.id, description: s.description, command: s.command, level: s.level, state: 'not-run' }));
176
+ const managedPrior = prior?.managedPaths ?? [];
177
+ const isManaged = (rel) => managedPrior.some((p) => rel === p || rel.startsWith(`${p}/`));
178
+ const managedNew = new Set(managedPrior);
179
+ let restartRequired = false;
180
+ let failure = null;
181
+ const hints = [];
182
+ await withLock(ws.dir, async () => {
183
+ await saveIntegrationEntry(ws, adapter.id, { installing: { pid: process.pid, startedAt: nowIso(), step: plan.steps[0].id }, lastAttemptAt: nowIso() }, nowIso());
184
+ const snap = await takeSnapshot(m.root, plan.protectPaths, ['.claude/skills'], isManaged);
185
+ for (let i = 0; i < plan.steps.length && !failure; i++) {
186
+ const step = plan.steps[i];
187
+ if (ac.signal.aborted) {
188
+ failure = 'cancelled';
189
+ break;
190
+ }
191
+ emit({ id: adapter.id, step: step.id, state: 'start', detail: step.description });
192
+ let out;
193
+ try {
194
+ out = await step.run();
195
+ }
196
+ catch (e) {
197
+ out = { ok: false, detail: e.message };
198
+ }
199
+ const violations = await verifySnapshot(snap);
200
+ if (violations.length > 0) {
201
+ await restoreSnapshot(snap, violations);
202
+ out = { ok: false, detail: `Refused: this step would have overwritten your files (${violations.map((v) => `${v.path}: ${v.reason}`).join('; ')}). They were restored.` };
203
+ }
204
+ const res = results[i];
205
+ res.state = out.ok ? 'ok' : 'failed';
206
+ res.detail = out.detail ? scrub(out.detail) : undefined;
207
+ if (!out.ok && out.hint)
208
+ hints.push(out.hint);
209
+ res.requiresApproval = out.requiresApproval;
210
+ emit({ id: adapter.id, step: step.id, state: out.ok ? 'ok' : 'fail', detail: res.detail ?? step.description });
211
+ if (out.ok) {
212
+ if (out.restartRequired)
213
+ restartRequired = true;
214
+ for (const p of out.managedPaths ?? [])
215
+ managedNew.add(p);
216
+ }
217
+ else
218
+ failure = res.detail ?? 'step failed';
219
+ }
220
+ }, { name: 'integrations.lock', staleMs: 30 * 60_000, timeoutMs: 10_000 });
221
+ const after = await adapterInspectAfter(ws, adapter, m, setup, emit, nowIso());
222
+ await saveIntegrationEntry(ws, adapter.id, {
223
+ installing: null,
224
+ lastError: failure,
225
+ managedPaths: [...managedNew].sort(),
226
+ ...(restartRequired && !failure ? { restartRequiredAt: nowIso() } : {}),
227
+ }, nowIso());
228
+ const finalState = (await loadIntegrationsState(ws, nowIso())).integrations[adapter.id] ?? blankEntry();
229
+ let finalReport = applyPersisted(after, finalState);
230
+ if (failure && finalReport.status !== 'VERIFIED')
231
+ finalReport = { ...finalReport, status: 'FAILED', warnings: [...finalReport.warnings, `Setup stopped: ${failure}`] };
232
+ if (hints.length > 0)
233
+ finalReport = { ...finalReport, nextSteps: [...hints, ...finalReport.nextSteps] };
234
+ const approvalStep = results.find((r) => r.requiresApproval);
235
+ if (approvalStep?.command)
236
+ finalReport = { ...finalReport, nextSteps: [`Run this yourself in a terminal (Claude Code needs your approval): ${approvalStep.command}`, ...finalReport.nextSteps] };
237
+ await saveIntegrationEntry(ws, adapter.id, { status: finalReport.status, variant: finalReport.variant, scope: finalReport.scope, version: finalReport.version, lastVerifiedAt: finalReport.status === 'VERIFIED' ? nowIso() : null }, nowIso());
238
+ outcomes.push(outcome(failure ? 'failed' : 'installed', finalReport, { plan: planInfo, steps: results, notes: plan.notes }));
239
+ }
240
+ }
241
+ finally {
242
+ disposeCancel();
243
+ }
244
+ const required = outcomes.filter((o) => o.report.required);
245
+ return {
246
+ dryRun: setup.dryRun,
247
+ scope: setup.scope,
248
+ outcomes,
249
+ events,
250
+ complete: required.length > 0 && required.every((o) => o.report.status === 'VERIFIED'),
251
+ approvalRequired,
252
+ };
253
+ }
254
+ async function adapterInspectAfter(ws, adapter, m, setup, emit, now) {
255
+ const state = await loadIntegrationsState(ws, now);
256
+ emit({ id: adapter.id, step: 'verify', state: 'start', detail: 'verifying the installation' });
257
+ const report = await adapter.inspect(ctxFor(m, setup, state.integrations[adapter.id], emit), true);
258
+ emit({ id: adapter.id, step: 'verify', state: report.status === 'VERIFIED' ? 'ok' : 'info', detail: report.status });
259
+ return report;
260
+ }
261
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/integrations/manager.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'node:os';\nimport type { Ws } from '../core/workspace.js';\nimport { onCleanup } from '../utils/cleanup.js';\nimport { withLock } from '../utils/lock.js';\nimport { redact } from '../security/redact.js';\nimport { findAbsolutePaths } from '../utils/paths.js';\nimport { agentSkills } from './adapters/agent-skills.js';\nimport { graphify } from './adapters/graphify.js';\nimport { omniroute } from './adapters/omniroute.js';\nimport { ponytail } from './adapters/ponytail.js';\nimport { createRunner } from './exec.js';\nimport { readPersistentPath, type PersistentPath } from './persistentPath.js';\nimport { restoreSnapshot, takeSnapshot, verifySnapshot } from './protect.js';\nimport { blankEntry, loadIntegrationsState, saveIntegrationEntry, wasInterrupted } from './state.js';\nimport {\n REQUIRED_INTEGRATIONS,\n type AdapterContext,\n type IntegrationAdapter,\n type IntegrationId,\n type IntegrationReport,\n type PersistedIntegration,\n type PlanStep,\n type ProgressEvent,\n type Runner,\n type SetupOptions,\n type StatusCode,\n type StepLevel,\n} from './types.js';\n\n/** Tool output goes into reports and the state file: strip secrets and machine-specific paths, flatten to one line. */\nexport function scrub(text: string): string {\n let out = redact(text);\n for (const p of findAbsolutePaths(out)) out = out.split(p).join('<path>');\n return out.replace(/\\s+/g, ' ').trim().slice(0, 400);\n}\n\nexport const ADAPTERS: IntegrationAdapter[] = [graphify, agentSkills, ponytail, omniroute];\n\nexport interface ManagerEnv {\n root: string;\n home?: string;\n env?: NodeJS.ProcessEnv;\n platform?: NodeJS.Platform;\n runner?: Runner;\n now?: () => Date;\n adapters?: IntegrationAdapter[];\n}\n\nexport interface StepResult {\n id: string;\n description: string;\n command?: string;\n level: StepLevel;\n state: 'ok' | 'failed' | 'not-run';\n detail?: string;\n requiresApproval?: boolean;\n}\n\nexport type OutcomeAction =\n | 'already-verified'\n | 'awaiting-restart'\n | 'nothing-to-do'\n | 'blocked'\n | 'dry-run'\n | 'approval-required'\n | 'installed'\n | 'failed'\n | 'not-selected';\n\nexport interface IntegrationOutcome {\n id: IntegrationId;\n name: string;\n action: OutcomeAction;\n before: StatusCode;\n report: IntegrationReport;\n plan: { description: string; command?: string; level: StepLevel }[];\n steps: StepResult[];\n notes: string[];\n}\n\nexport interface SetupResult {\n dryRun: boolean;\n scope: SetupOptions['scope'];\n outcomes: IntegrationOutcome[];\n events: ProgressEvent[];\n /** Every requested required integration is VERIFIED. */\n complete: boolean;\n approvalRequired: boolean;\n}\n\nfunction resolveEnv(m: ManagerEnv): Required<Pick<ManagerEnv, 'root' | 'home' | 'env' | 'platform' | 'runner' | 'now' | 'adapters'>> {\n const env = m.env ?? process.env;\n const platform = m.platform ?? process.platform;\n return {\n root: m.root,\n home: m.home ?? os.homedir(),\n env,\n platform,\n runner: m.runner ?? createRunner(env, platform),\n now: m.now ?? (() => new Date()),\n adapters: m.adapters ?? ADAPTERS,\n };\n}\n\nfunction ctxFor(m: ReturnType<typeof resolveEnv>, setup: SetupOptions, prior: PersistedIntegration | undefined, report: (e: ProgressEvent) => void, signal?: AbortSignal): AdapterContext {\n let persistent: Promise<PersistentPath | null> | undefined;\n return {\n root: m.root,\n home: m.home,\n env: m.env,\n platform: m.platform,\n runner: m.runner,\n setup,\n signal,\n prior,\n report,\n persistentPath: () => (persistent ??= readPersistentPath(m.runner, m.platform, m.env, signal)),\n };\n}\n\nconst DEFAULT_SETUP: SetupOptions = { scope: 'project', yes: false, dryRun: false, repair: false };\n\n/** Combine what was observed with what earlier runs recorded. Records can only add warnings or RESTART_REQUIRED, never VERIFIED. */\nexport function applyPersisted(report: IntegrationReport, entry: PersistedIntegration | undefined): IntegrationReport {\n if (!entry) return report;\n const out: IntegrationReport = { ...report, warnings: [...report.warnings], nextSteps: [...report.nextSteps] };\n if (wasInterrupted(entry)) {\n out.warnings.push('A previous setup run was interrupted before it finished.');\n if (!out.nextSteps.includes('devworkspace integrations repair --yes')) out.nextSteps.push('devworkspace integrations repair --yes');\n }\n if ((out.status === 'NOT_INSTALLED' || out.status === 'DEGRADED') && entry.lastError) {\n out.status = 'FAILED';\n out.warnings.push(`The last setup attempt failed: ${entry.lastError}`);\n }\n if (entry.restartRequiredAt && (out.status === 'VERIFIED' || out.status === 'INSTALLED')) {\n out.status = 'RESTART_REQUIRED';\n out.nextSteps.push('Restart Claude Code (or run /reload-plugins) so it loads the new skills/plugins/hooks, then run: devworkspace integrations status --restarted');\n }\n return out;\n}\n\nexport async function inspectAll(ws: Ws, mEnv: ManagerEnv, opts: { deep: boolean; onlyRequired?: boolean } = { deep: false }): Promise<IntegrationReport[]> {\n const m = resolveEnv(mEnv);\n const state = await loadIntegrationsState(ws, m.now().toISOString());\n const list = m.adapters.filter((a) => !opts.onlyRequired || a.required);\n return Promise.all(\n list.map(async (a) => {\n const ctx = ctxFor(m, DEFAULT_SETUP, state.integrations[a.id], () => undefined);\n try {\n return applyPersisted(await a.inspect(ctx, opts.deep), state.integrations[a.id]);\n } catch (e) {\n return {\n id: a.id,\n name: a.name,\n required: a.required,\n status: 'FAILED' as const,\n variant: null,\n scope: null,\n version: null,\n checks: [],\n capabilities: {},\n warnings: [`Inspection failed: ${scrub((e as Error).message)}`],\n nextSteps: ['devworkspace doctor --debug'],\n deep: opts.deep,\n };\n }\n }),\n );\n}\n\n/** Summary used by doctor/status: required integrations only; OmniRoute never counts. */\nexport function summarize(reports: IntegrationReport[]): { complete: boolean; required: number; verified: number; pending: IntegrationReport[] } {\n const required = reports.filter((r) => r.required);\n const pending = required.filter((r) => r.status !== 'VERIFIED');\n return { complete: pending.length === 0, required: required.length, verified: required.length - pending.length, pending };\n}\n\nexport async function acknowledgeRestart(ws: Ws, ids: IntegrationId[], now: string): Promise<void> {\n for (const id of ids) await saveIntegrationEntry(ws, id, { restartRequiredAt: null }, now);\n}\n\nconst describeStep = (s: PlanStep): { description: string; command?: string; level: StepLevel } => ({ description: s.description, command: s.command, level: s.level });\n\nexport async function runSetup(ws: Ws, mEnv: ManagerEnv, setup: SetupOptions, onEvent: (e: ProgressEvent) => void = () => undefined): Promise<SetupResult> {\n const m = resolveEnv(mEnv);\n const events: ProgressEvent[] = [];\n const emit = (e: ProgressEvent): void => {\n events.push(e);\n onEvent(e);\n };\n const ac = new AbortController();\n const disposeCancel = onCleanup(() => ac.abort());\n const wanted = m.adapters.filter((a) => (setup.only ? setup.only.includes(a.id) : REQUIRED_INTEGRATIONS.includes(a.id)));\n const outcomes: IntegrationOutcome[] = [];\n let approvalRequired = false;\n\n try {\n for (const adapter of wanted) {\n const nowIso = (): string => m.now().toISOString();\n const state = await loadIntegrationsState(ws, nowIso());\n const prior = state.integrations[adapter.id];\n const ctx = ctxFor(m, setup, prior, emit, ac.signal);\n emit({ id: adapter.id, step: 'inspect', state: 'start', detail: `checking ${adapter.name}` });\n const before = applyPersisted(await adapter.inspect(ctx, true), prior);\n const outcome = (action: OutcomeAction, report: IntegrationReport, extra: Partial<IntegrationOutcome> = {}): IntegrationOutcome => ({\n id: adapter.id,\n name: adapter.name,\n action,\n before: before.status,\n report,\n plan: [],\n steps: [],\n notes: [],\n ...extra,\n });\n\n if (before.status === 'VERIFIED' && !setup.repair) {\n emit({ id: adapter.id, step: 'inspect', state: 'skip', detail: 'already verified' });\n outcomes.push(outcome('already-verified', before));\n continue;\n }\n if (before.status === 'RESTART_REQUIRED' && !setup.repair) {\n emit({ id: adapter.id, step: 'inspect', state: 'skip', detail: 'installed; waiting for a Claude Code restart' });\n outcomes.push(outcome('awaiting-restart', before));\n continue;\n }\n\n const plan = await adapter.plan(ctx, before);\n if (plan.steps.length === 0) {\n if (plan.blockers.length > 0) {\n emit({ id: adapter.id, step: 'prerequisites', state: 'fail', detail: plan.blockers[0] });\n outcomes.push(outcome('blocked', { ...before, status: 'PREREQUISITES_MISSING', nextSteps: plan.blockers }, { notes: plan.notes }));\n } else {\n outcomes.push(outcome('nothing-to-do', before, { notes: plan.notes }));\n }\n continue;\n }\n const planInfo = plan.steps.map(describeStep);\n if (setup.dryRun) {\n outcomes.push(outcome('dry-run', before, { plan: planInfo, notes: plan.notes }));\n continue;\n }\n const needsConsent = plan.steps.some((s) => s.level !== 'project');\n if (needsConsent && !setup.yes) {\n approvalRequired = true;\n const levels = [...new Set(plan.steps.filter((s) => s.level !== 'project').map((s) => s.level))];\n outcomes.push(\n outcome('approval-required', { ...before, nextSteps: [`Re-run with --yes to approve: ${levels.join(', ')} step(s). Review them first with --dry-run.`, ...before.nextSteps] }, { plan: planInfo, notes: plan.notes }),\n );\n continue;\n }\n\n // ---- execute ----\n const results: StepResult[] = plan.steps.map((s) => ({ id: s.id, description: s.description, command: s.command, level: s.level, state: 'not-run' as const }));\n const managedPrior = prior?.managedPaths ?? [];\n const isManaged = (rel: string): boolean => managedPrior.some((p) => rel === p || rel.startsWith(`${p}/`));\n const managedNew = new Set<string>(managedPrior);\n let restartRequired = false;\n let failure: string | null = null;\n const hints: string[] = [];\n\n await withLock(\n ws.dir,\n async () => {\n await saveIntegrationEntry(ws, adapter.id, { installing: { pid: process.pid, startedAt: nowIso(), step: plan.steps[0]!.id }, lastAttemptAt: nowIso() }, nowIso());\n const snap = await takeSnapshot(m.root, plan.protectPaths, ['.claude/skills'], isManaged);\n for (let i = 0; i < plan.steps.length && !failure; i++) {\n const step = plan.steps[i]!;\n if (ac.signal.aborted) {\n failure = 'cancelled';\n break;\n }\n emit({ id: adapter.id, step: step.id, state: 'start', detail: step.description });\n let out;\n try {\n out = await step.run();\n } catch (e) {\n out = { ok: false, detail: (e as Error).message };\n }\n const violations = await verifySnapshot(snap);\n if (violations.length > 0) {\n await restoreSnapshot(snap, violations);\n out = { ok: false, detail: `Refused: this step would have overwritten your files (${violations.map((v) => `${v.path}: ${v.reason}`).join('; ')}). They were restored.` };\n }\n const res = results[i]!;\n res.state = out.ok ? 'ok' : 'failed';\n res.detail = out.detail ? scrub(out.detail) : undefined;\n if (!out.ok && out.hint) hints.push(out.hint);\n res.requiresApproval = out.requiresApproval;\n emit({ id: adapter.id, step: step.id, state: out.ok ? 'ok' : 'fail', detail: res.detail ?? step.description });\n if (out.ok) {\n if (out.restartRequired) restartRequired = true;\n for (const p of out.managedPaths ?? []) managedNew.add(p);\n } else failure = res.detail ?? 'step failed';\n }\n },\n { name: 'integrations.lock', staleMs: 30 * 60_000, timeoutMs: 10_000 },\n );\n\n const after = await adapterInspectAfter(ws, adapter, m, setup, emit, nowIso());\n await saveIntegrationEntry(\n ws,\n adapter.id,\n {\n installing: null,\n lastError: failure,\n managedPaths: [...managedNew].sort(),\n ...(restartRequired && !failure ? { restartRequiredAt: nowIso() } : {}),\n },\n nowIso(),\n );\n const finalState = (await loadIntegrationsState(ws, nowIso())).integrations[adapter.id] ?? blankEntry();\n let finalReport = applyPersisted(after, finalState);\n if (failure && finalReport.status !== 'VERIFIED') finalReport = { ...finalReport, status: 'FAILED', warnings: [...finalReport.warnings, `Setup stopped: ${failure}`] };\n if (hints.length > 0) finalReport = { ...finalReport, nextSteps: [...hints, ...finalReport.nextSteps] };\n const approvalStep = results.find((r) => r.requiresApproval);\n if (approvalStep?.command) finalReport = { ...finalReport, nextSteps: [`Run this yourself in a terminal (Claude Code needs your approval): ${approvalStep.command}`, ...finalReport.nextSteps] };\n await saveIntegrationEntry(\n ws,\n adapter.id,\n { status: finalReport.status, variant: finalReport.variant, scope: finalReport.scope, version: finalReport.version, lastVerifiedAt: finalReport.status === 'VERIFIED' ? nowIso() : null },\n nowIso(),\n );\n outcomes.push(outcome(failure ? 'failed' : 'installed', finalReport, { plan: planInfo, steps: results, notes: plan.notes }));\n }\n } finally {\n disposeCancel();\n }\n\n const required = outcomes.filter((o) => o.report.required);\n return {\n dryRun: setup.dryRun,\n scope: setup.scope,\n outcomes,\n events,\n complete: required.length > 0 && required.every((o) => o.report.status === 'VERIFIED'),\n approvalRequired,\n };\n}\n\nasync function adapterInspectAfter(ws: Ws, adapter: IntegrationAdapter, m: ReturnType<typeof resolveEnv>, setup: SetupOptions, emit: (e: ProgressEvent) => void, now: string): Promise<IntegrationReport> {\n const state = await loadIntegrationsState(ws, now);\n emit({ id: adapter.id, step: 'verify', state: 'start', detail: 'verifying the installation' });\n const report = await adapter.inspect(ctxFor(m, setup, state.integrations[adapter.id], emit), true);\n emit({ id: adapter.id, step: 'verify', state: report.status === 'VERIFIED' ? 'ok' : 'info', detail: report.status });\n return report;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ export interface NoticeSource {
2
+ name: string;
3
+ repo: string;
4
+ license: string;
5
+ copyright: string;
6
+ /** What DevWorkspace installed from it. */
7
+ installed: string;
8
+ }
9
+ /** Idempotently add (or refresh) one project's notice block. Only text between its own markers is ever replaced. */
10
+ export declare function writeThirdPartyNotice(root: string, source: NoticeSource): Promise<void>;
11
+ export declare const AGENT_SKILLS_NOTICE: NoticeSource;
12
+ export declare const PONYTAIL_NOTICE: NoticeSource;
13
+ export declare const GRAPHIFY_NOTICE: NoticeSource;
@@ -0,0 +1,68 @@
1
+ import path from 'node:path';
2
+ import { promises as fs } from 'node:fs';
3
+ import { atomicWriteFile } from '../utils/fs.js';
4
+ const HEADER = [
5
+ '# Third-party notices',
6
+ '',
7
+ 'DevWorkspace can install the following third-party software into this project or onto this machine.',
8
+ 'DevWorkspace does not vendor or modify their code; files are copied unchanged by their own installers.',
9
+ 'Each project remains under its own license. Keep this notice if you commit the installed files.',
10
+ 'Text outside the marked blocks is yours and is never modified.',
11
+ '',
12
+ ].join('\n');
13
+ const startMarker = (name) => `<!-- devworkspace:notice:start ${name} -->`;
14
+ const endMarker = (name) => `<!-- devworkspace:notice:end ${name} -->`;
15
+ function entry(s) {
16
+ return [
17
+ startMarker(s.name),
18
+ `## ${s.name}`,
19
+ `- Source: https://github.com/${s.repo}`,
20
+ `- License: ${s.license}`,
21
+ `- Copyright: ${s.copyright}`,
22
+ `- Installed by DevWorkspace: ${s.installed}`,
23
+ endMarker(s.name),
24
+ ].join('\n');
25
+ }
26
+ /** Idempotently add (or refresh) one project's notice block. Only text between its own markers is ever replaced. */
27
+ export async function writeThirdPartyNotice(root, source) {
28
+ const file = path.join(root, '.devworkspace', 'THIRD_PARTY_NOTICES.md');
29
+ let text = HEADER;
30
+ try {
31
+ text = await fs.readFile(file, 'utf8');
32
+ }
33
+ catch {
34
+ /* new file */
35
+ }
36
+ const start = text.indexOf(startMarker(source.name));
37
+ const end = text.indexOf(endMarker(source.name));
38
+ const block = entry(source);
39
+ if (start !== -1 && end > start) {
40
+ text = text.slice(0, start) + block + text.slice(end + endMarker(source.name).length);
41
+ }
42
+ else {
43
+ text = text.replace(/\n*$/, '\n\n') + block + '\n';
44
+ }
45
+ await atomicWriteFile(file, text);
46
+ }
47
+ export const AGENT_SKILLS_NOTICE = {
48
+ name: 'Agent Skills',
49
+ repo: 'addyosmani/agent-skills',
50
+ license: 'MIT',
51
+ copyright: 'Copyright (c) 2025 Addy Osmani',
52
+ installed: 'engineering skills copied into .claude/skills/ (portable skills, not the native plugin)',
53
+ };
54
+ export const PONYTAIL_NOTICE = {
55
+ name: 'Ponytail',
56
+ repo: 'DietrichGebert/ponytail',
57
+ license: 'MIT',
58
+ copyright: 'Copyright (c) 2026 DietrichGebert',
59
+ installed: 'skills copied into .claude/skills/ (portable skills, not the native plugin; no hooks, commands or always-on ruleset)',
60
+ };
61
+ export const GRAPHIFY_NOTICE = {
62
+ name: 'Graphify',
63
+ repo: 'Graphify-Labs/graphify',
64
+ license: 'see the project repository and the graphifyy package metadata',
65
+ copyright: 'see the project repository',
66
+ installed: 'the graphifyy command-line tool (user-level) and its project skill in .claude/skills/graphify/',
67
+ };
68
+ //# sourceMappingURL=notices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notices.js","sourceRoot":"","sources":["../../src/integrations/notices.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport { atomicWriteFile } from '../utils/fs.js';\n\nexport interface NoticeSource {\n name: string;\n repo: string;\n license: string;\n copyright: string;\n /** What DevWorkspace installed from it. */\n installed: string;\n}\n\nconst HEADER = [\n '# Third-party notices',\n '',\n 'DevWorkspace can install the following third-party software into this project or onto this machine.',\n 'DevWorkspace does not vendor or modify their code; files are copied unchanged by their own installers.',\n 'Each project remains under its own license. Keep this notice if you commit the installed files.',\n 'Text outside the marked blocks is yours and is never modified.',\n '',\n].join('\\n');\n\nconst startMarker = (name: string): string => `<!-- devworkspace:notice:start ${name} -->`;\nconst endMarker = (name: string): string => `<!-- devworkspace:notice:end ${name} -->`;\n\nfunction entry(s: NoticeSource): string {\n return [\n startMarker(s.name),\n `## ${s.name}`,\n `- Source: https://github.com/${s.repo}`,\n `- License: ${s.license}`,\n `- Copyright: ${s.copyright}`,\n `- Installed by DevWorkspace: ${s.installed}`,\n endMarker(s.name),\n ].join('\\n');\n}\n\n/** Idempotently add (or refresh) one project's notice block. Only text between its own markers is ever replaced. */\nexport async function writeThirdPartyNotice(root: string, source: NoticeSource): Promise<void> {\n const file = path.join(root, '.devworkspace', 'THIRD_PARTY_NOTICES.md');\n let text = HEADER;\n try {\n text = await fs.readFile(file, 'utf8');\n } catch {\n /* new file */\n }\n const start = text.indexOf(startMarker(source.name));\n const end = text.indexOf(endMarker(source.name));\n const block = entry(source);\n if (start !== -1 && end > start) {\n text = text.slice(0, start) + block + text.slice(end + endMarker(source.name).length);\n } else {\n text = text.replace(/\\n*$/, '\\n\\n') + block + '\\n';\n }\n await atomicWriteFile(file, text);\n}\n\nexport const AGENT_SKILLS_NOTICE: NoticeSource = {\n name: 'Agent Skills',\n repo: 'addyosmani/agent-skills',\n license: 'MIT',\n copyright: 'Copyright (c) 2025 Addy Osmani',\n installed: 'engineering skills copied into .claude/skills/ (portable skills, not the native plugin)',\n};\n\nexport const PONYTAIL_NOTICE: NoticeSource = {\n name: 'Ponytail',\n repo: 'DietrichGebert/ponytail',\n license: 'MIT',\n copyright: 'Copyright (c) 2026 DietrichGebert',\n installed: 'skills copied into .claude/skills/ (portable skills, not the native plugin; no hooks, commands or always-on ruleset)',\n};\n\nexport const GRAPHIFY_NOTICE: NoticeSource = {\n name: 'Graphify',\n repo: 'Graphify-Labs/graphify',\n license: 'see the project repository and the graphifyy package metadata',\n copyright: 'see the project repository',\n installed: 'the graphifyy command-line tool (user-level) and its project skill in .claude/skills/graphify/',\n};\n"]}
@@ -0,0 +1,20 @@
1
+ import type { Runner } from './types.js';
2
+ /**
3
+ * The PATH Windows will give to NEW processes, as stored in the registry. A running terminal or editor keeps the PATH
4
+ * it started with, so it can differ from this. Read-only: nothing here ever writes to the registry or the environment.
5
+ */
6
+ export interface PersistentPath {
7
+ user: string[];
8
+ machine: string[];
9
+ }
10
+ /** Extract the value of `Path` from `reg query <key> /v Path` output. */
11
+ export declare function parseRegPath(output: string): string | null;
12
+ /** Expand %NAME% references the way Windows does (case-insensitive); unknown names are left untouched. */
13
+ export declare function expandWindowsVars(value: string, env: NodeJS.ProcessEnv): string;
14
+ export declare function splitPathList(value: string): string[];
15
+ /** Windows paths are case-insensitive and tolerate trailing separators and `.`/`..` segments. */
16
+ export declare function normalizeWindowsDir(dir: string): string;
17
+ /** Windows only. Returns null on every other platform, or when the user PATH cannot be read (unknown, not "absent"). */
18
+ export declare function readPersistentPath(runner: Runner, platform: NodeJS.Platform, env: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PersistentPath | null>;
19
+ /** Which persistent PATH (if any) lists this directory. */
20
+ export declare function persistentPathHas(pp: PersistentPath, dir: string): 'user' | 'machine' | null;
@@ -0,0 +1,47 @@
1
+ import path from 'node:path';
2
+ const USER_KEY = 'HKCU\\Environment';
3
+ const MACHINE_KEY = 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment';
4
+ /** Extract the value of `Path` from `reg query <key> /v Path` output. */
5
+ export function parseRegPath(output) {
6
+ const m = /^\s*Path\s+REG_(?:EXPAND_)?SZ\s+(.*?)\s*$/im.exec(output);
7
+ return m && m[1] !== undefined ? m[1] : null;
8
+ }
9
+ /** Expand %NAME% references the way Windows does (case-insensitive); unknown names are left untouched. */
10
+ export function expandWindowsVars(value, env) {
11
+ const lower = new Map(Object.entries(env).map(([k, v]) => [k.toLowerCase(), v ?? '']));
12
+ return value.replace(/%([^%]+)%/g, (whole, name) => lower.get(name.toLowerCase()) ?? whole);
13
+ }
14
+ export function splitPathList(value) {
15
+ return value.split(';').map((s) => s.trim().replace(/^"(.*)"$/, '$1')).filter(Boolean);
16
+ }
17
+ /** Windows paths are case-insensitive and tolerate trailing separators and `.`/`..` segments. */
18
+ export function normalizeWindowsDir(dir) {
19
+ return path.win32.normalize(dir).replace(/[\\/]+$/, '').toLowerCase();
20
+ }
21
+ async function readKey(runner, key, env, signal) {
22
+ const r = await runner.run({ command: 'reg', args: ['query', key, '/v', 'Path'], timeoutMs: 10_000, signal });
23
+ if (!r.ok)
24
+ return null;
25
+ const raw = parseRegPath(r.stdout);
26
+ return raw === null ? [] : splitPathList(expandWindowsVars(raw, env));
27
+ }
28
+ /** Windows only. Returns null on every other platform, or when the user PATH cannot be read (unknown, not "absent"). */
29
+ export async function readPersistentPath(runner, platform, env, signal) {
30
+ if (platform !== 'win32')
31
+ return null;
32
+ const user = await readKey(runner, USER_KEY, env, signal);
33
+ if (user === null)
34
+ return null;
35
+ const machine = (await readKey(runner, MACHINE_KEY, env, signal)) ?? [];
36
+ return { user, machine };
37
+ }
38
+ /** Which persistent PATH (if any) lists this directory. */
39
+ export function persistentPathHas(pp, dir) {
40
+ const want = normalizeWindowsDir(dir);
41
+ if (pp.user.some((p) => normalizeWindowsDir(p) === want))
42
+ return 'user';
43
+ if (pp.machine.some((p) => normalizeWindowsDir(p) === want))
44
+ return 'machine';
45
+ return null;
46
+ }
47
+ //# sourceMappingURL=persistentPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistentPath.js","sourceRoot":"","sources":["../../src/integrations/persistentPath.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport type { Runner } from './types.js';\n\n/**\n * The PATH Windows will give to NEW processes, as stored in the registry. A running terminal or editor keeps the PATH\n * it started with, so it can differ from this. Read-only: nothing here ever writes to the registry or the environment.\n */\nexport interface PersistentPath {\n user: string[];\n machine: string[];\n}\n\nconst USER_KEY = 'HKCU\\\\Environment';\nconst MACHINE_KEY = 'HKLM\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment';\n\n/** Extract the value of `Path` from `reg query <key> /v Path` output. */\nexport function parseRegPath(output: string): string | null {\n const m = /^\\s*Path\\s+REG_(?:EXPAND_)?SZ\\s+(.*?)\\s*$/im.exec(output);\n return m && m[1] !== undefined ? m[1] : null;\n}\n\n/** Expand %NAME% references the way Windows does (case-insensitive); unknown names are left untouched. */\nexport function expandWindowsVars(value: string, env: NodeJS.ProcessEnv): string {\n const lower = new Map(Object.entries(env).map(([k, v]) => [k.toLowerCase(), v ?? '']));\n return value.replace(/%([^%]+)%/g, (whole, name: string) => lower.get(name.toLowerCase()) ?? whole);\n}\n\nexport function splitPathList(value: string): string[] {\n return value.split(';').map((s) => s.trim().replace(/^\"(.*)\"$/, '$1')).filter(Boolean);\n}\n\n/** Windows paths are case-insensitive and tolerate trailing separators and `.`/`..` segments. */\nexport function normalizeWindowsDir(dir: string): string {\n return path.win32.normalize(dir).replace(/[\\\\/]+$/, '').toLowerCase();\n}\n\nasync function readKey(runner: Runner, key: string, env: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<string[] | null> {\n const r = await runner.run({ command: 'reg', args: ['query', key, '/v', 'Path'], timeoutMs: 10_000, signal });\n if (!r.ok) return null;\n const raw = parseRegPath(r.stdout);\n return raw === null ? [] : splitPathList(expandWindowsVars(raw, env));\n}\n\n/** Windows only. Returns null on every other platform, or when the user PATH cannot be read (unknown, not \"absent\"). */\nexport async function readPersistentPath(runner: Runner, platform: NodeJS.Platform, env: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PersistentPath | null> {\n if (platform !== 'win32') return null;\n const user = await readKey(runner, USER_KEY, env, signal);\n if (user === null) return null;\n const machine = (await readKey(runner, MACHINE_KEY, env, signal)) ?? [];\n return { user, machine };\n}\n\n/** Which persistent PATH (if any) lists this directory. */\nexport function persistentPathHas(pp: PersistentPath, dir: string): 'user' | 'machine' | null {\n const want = normalizeWindowsDir(dir);\n if (pp.user.some((p) => normalizeWindowsDir(p) === want)) return 'user';\n if (pp.machine.some((p) => normalizeWindowsDir(p) === want)) return 'machine';\n return null;\n}\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Installers (graphify, claude plugin, skills CLI) edit files the user owns. Before a step runs we snapshot the files
3
+ * it may touch, and afterwards verify that everything the user had is still there. If not, we restore the snapshot.
4
+ */
5
+ export interface Snapshot {
6
+ root: string;
7
+ /** rel path -> original text, or null when the file did not exist. */
8
+ files: Map<string, string | null>;
9
+ /** Existing files below these directories must stay byte-identical. */
10
+ trees: Map<string, string>;
11
+ }
12
+ export interface Violation {
13
+ path: string;
14
+ reason: string;
15
+ }
16
+ export declare function takeSnapshot(root: string, files: string[], trees?: string[], skipInTrees?: (rel: string) => boolean): Promise<Snapshot>;
17
+ /** True if every key/element of `before` is still present in `after` (after may add more). */
18
+ export declare function jsonExtends(before: unknown, after: unknown): boolean;
19
+ export declare function verifySnapshot(snap: Snapshot): Promise<Violation[]>;
20
+ export declare function restoreSnapshot(snap: Snapshot, violations: Violation[]): Promise<void>;