@duckmind/dm-darwin-x64 0.13.2 → 0.13.6

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 (41) hide show
  1. package/CHANGELOG.md +49 -2
  2. package/README.md +5 -5
  3. package/dm +0 -0
  4. package/docs/settings.md +1 -0
  5. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  6. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  7. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  8. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  9. package/examples/extensions/with-deps/package-lock.json +2 -2
  10. package/examples/extensions/with-deps/package.json +1 -1
  11. package/extensions/.dm-extensions.json +2 -2
  12. package/extensions/dm-multicodex/README.md +3 -1
  13. package/extensions/dm-multicodex/account-manager.test.ts +3 -1
  14. package/extensions/dm-multicodex/account-manager.ts +27 -1
  15. package/extensions/dm-multicodex/commands.test.ts +1 -0
  16. package/extensions/dm-multicodex/commands.ts +81 -1
  17. package/extensions/dm-multicodex/index.ts +7 -0
  18. package/extensions/dm-multicodex/node_modules/.package-lock.json +28 -28
  19. package/extensions/dm-multicodex/package-lock.json +9633 -9633
  20. package/extensions/dm-multicodex/package.json +56 -56
  21. package/extensions/dm-multicodex/storage.ts +2 -2
  22. package/extensions/dm-multicodex/sync.test.ts +114 -0
  23. package/extensions/dm-multicodex/sync.ts +249 -0
  24. package/extensions/dm-multicodex/tsconfig.json +17 -0
  25. package/extensions/dm-subagents/artifacts.ts +11 -5
  26. package/extensions/dm-subagents/async-execution.ts +6 -1
  27. package/extensions/dm-subagents/execution.ts +1 -1
  28. package/extensions/dm-subagents/index.ts +1 -1
  29. package/extensions/dm-subagents/intercom-bridge.ts +8 -0
  30. package/extensions/dm-subagents/package.json +1 -1
  31. package/extensions/dm-subagents/schemas.ts +1 -1
  32. package/extensions/dm-subagents/settings.ts +6 -4
  33. package/extensions/dm-subagents/skills.ts +117 -25
  34. package/extensions/dm-subagents/subagent-executor.ts +2 -6
  35. package/extensions/dm-subagents/subagent-runner.ts +176 -51
  36. package/extensions/dm-subagents/types.ts +62 -2
  37. package/extensions/dm-subagents/worktree.ts +27 -9
  38. package/extensions/dm-thinking-timer/README.md +1 -1
  39. package/extensions/dm-ultrathink/src/naming.ts +151 -10
  40. package/package.json +1 -1
  41. package/theme/duckmind.json +77 -65
@@ -106,9 +106,11 @@ function resolveRepoState(cwd: string): RepoState {
106
106
  }
107
107
 
108
108
  const toplevel = runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim();
109
- const realCwd = fs.realpathSync(cwd);
110
- const realToplevel = fs.realpathSync(toplevel);
111
- const cwdRelative = path.relative(realToplevel, realCwd);
109
+ const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
110
+ const normalizedPrefix = rawPrefix
111
+ ? path.normalize(rawPrefix.replace(/[\\/]+$/, ""))
112
+ : "";
113
+ const cwdRelative = normalizedPrefix === "." ? "" : normalizedPrefix;
112
114
 
113
115
  const status = runGitChecked(toplevel, ["status", "--porcelain"]);
114
116
  if (status.trim().length > 0) {
@@ -124,6 +126,7 @@ function normalizeComparableCwd(cwd: string): string {
124
126
  try {
125
127
  return fs.realpathSync(resolved);
126
128
  } catch {
129
+ // Use the unresolved absolute path when realpath resolution is unavailable.
127
130
  return resolved;
128
131
  }
129
132
  }
@@ -169,6 +172,7 @@ function linkNodeModulesIfPresent(toplevel: string, worktreePath: string): boole
169
172
  fs.symlinkSync(nodeModulesPath, nodeModulesLinkPath);
170
173
  return true;
171
174
  } catch {
175
+ // Symlink creation is optional (e.g., unsupported filesystems on CI runners).
172
176
  return false;
173
177
  }
174
178
  }
@@ -344,8 +348,12 @@ function createSingleWorktree(
344
348
  syntheticPaths,
345
349
  };
346
350
  } catch (error) {
347
- try { runGitChecked(toplevel, ["worktree", "remove", "--force", worktreePath]); } catch {}
348
- try { runGitChecked(toplevel, ["branch", "-D", branch]); } catch {}
351
+ try { runGitChecked(toplevel, ["worktree", "remove", "--force", worktreePath]); } catch {
352
+ // Best-effort rollback; preserve the original setup failure.
353
+ }
354
+ try { runGitChecked(toplevel, ["branch", "-D", branch]); } catch {
355
+ // Best-effort rollback; preserve the original setup failure.
356
+ }
349
357
  throw error;
350
358
  }
351
359
  }
@@ -453,12 +461,18 @@ function captureWorktreeDiff(
453
461
  function writeEmptyPatch(patchPath: string): void {
454
462
  try {
455
463
  fs.writeFileSync(patchPath, "", "utf-8");
456
- } catch {}
464
+ } catch {
465
+ // Diff artifact writing is best-effort in error paths.
466
+ }
457
467
  }
458
468
 
459
469
  function cleanupSingleWorktree(repoCwd: string, worktree: WorktreeInfo): void {
460
- try { runGitChecked(repoCwd, ["worktree", "remove", "--force", worktree.path]); } catch {}
461
- try { runGitChecked(repoCwd, ["branch", "-D", worktree.branch]); } catch {}
470
+ try { runGitChecked(repoCwd, ["worktree", "remove", "--force", worktree.path]); } catch {
471
+ // Cleanup is best-effort to avoid masking caller errors.
472
+ }
473
+ try { runGitChecked(repoCwd, ["branch", "-D", worktree.branch]); } catch {
474
+ // Cleanup is best-effort to avoid masking caller errors.
475
+ }
462
476
  }
463
477
 
464
478
  function hasWorktreeChanges(diff: WorktreeDiff): boolean {
@@ -502,6 +516,7 @@ export function diffWorktrees(setup: WorktreeSetup, agents: string[], diffsDir:
502
516
  try {
503
517
  fs.mkdirSync(diffsDir, { recursive: true });
504
518
  } catch {
519
+ // Returning no diffs is safer than failing the whole command on artifact-dir issues.
505
520
  return [];
506
521
  }
507
522
 
@@ -513,6 +528,7 @@ export function diffWorktrees(setup: WorktreeSetup, agents: string[], diffsDir:
513
528
  try {
514
529
  diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
515
530
  } catch {
531
+ // Preserve execution flow; failed diff capture maps to an empty per-task patch.
516
532
  writeEmptyPatch(patchPath);
517
533
  diffs.push(emptyDiff(index, agent, worktree.branch, patchPath));
518
534
  }
@@ -525,7 +541,9 @@ export function cleanupWorktrees(setup: WorktreeSetup): void {
525
541
  for (let index = setup.worktrees.length - 1; index >= 0; index--) {
526
542
  cleanupSingleWorktree(setup.cwd, setup.worktrees[index]!);
527
543
  }
528
- try { runGitChecked(setup.cwd, ["worktree", "prune"]); } catch {}
544
+ try { runGitChecked(setup.cwd, ["worktree", "prune"]); } catch {
545
+ // Pruning is best-effort cleanup.
546
+ }
529
547
  }
530
548
 
531
549
  export function formatWorktreeDiffSummary(diffs: WorktreeDiff[]): string {
@@ -4,4 +4,4 @@ Bundled DM extension that shows a live timer next to collapsed Thinking blocks.
4
4
 
5
5
  ## Bundled with dm
6
6
 
7
- No separate install step is required. Install `dm`, restart the TUI or run `/reload`, and the timer patch is available.
7
+ No separate install step is required. Install `dm` and restart the TUI if the timer patch is not already active.
@@ -45,6 +45,55 @@ type NamingTestOverrides = {
45
45
  generateMergeCommitMessage?: (args: GenerateMergeCommitMessageArgs) => Promise<GeneratedCommitMessage>;
46
46
  };
47
47
 
48
+ type NamingModelChoice = {
49
+ label: string;
50
+ config: NamingModelConfig;
51
+ };
52
+
53
+ type DuckMindPresetAlias = "free" | "auto" | "lite" | "smart" | "deep";
54
+
55
+ type DuckMindPreset = {
56
+ alias: DuckMindPresetAlias;
57
+ envKey: string;
58
+ fallbackModelId: string;
59
+ displayName: string;
60
+ };
61
+
62
+ const DUCKMIND_PROVIDER = "duckmind";
63
+ const OPENROUTER_PROVIDER = "openrouter";
64
+ const DUCKMIND_PRESETS: readonly DuckMindPreset[] = [
65
+ {
66
+ alias: "free",
67
+ envKey: "DM_FREE_MODEL",
68
+ fallbackModelId: "google/gemini-3.1-flash-lite-preview",
69
+ displayName: "DuckMind Free",
70
+ },
71
+ {
72
+ alias: "auto",
73
+ envKey: "DM_AUTO_MODEL",
74
+ fallbackModelId: "auto",
75
+ displayName: "DuckMind Auto",
76
+ },
77
+ {
78
+ alias: "lite",
79
+ envKey: "DM_LITE_MODEL",
80
+ fallbackModelId: "google/gemini-2.5-flash",
81
+ displayName: "DuckMind Lite",
82
+ },
83
+ {
84
+ alias: "smart",
85
+ envKey: "DM_SMART_MODEL",
86
+ fallbackModelId: "anthropic/claude-sonnet-4.5",
87
+ displayName: "DuckMind Smart",
88
+ },
89
+ {
90
+ alias: "deep",
91
+ envKey: "DM_DEEP_MODEL",
92
+ fallbackModelId: "openai/o3",
93
+ displayName: "DuckMind Deep",
94
+ },
95
+ ];
96
+
48
97
  let testOverrides: NamingTestOverrides | undefined;
49
98
 
50
99
  export function setNamingTestOverrides(overrides?: NamingTestOverrides): void {
@@ -102,8 +151,90 @@ function normalizeCommitMessage(message: GeneratedCommitMessage): GeneratedCommi
102
151
  return { subject, body };
103
152
  }
104
153
 
154
+ function resolveDuckMindPresetModelId(envKey: string, fallbackModelId: string): string {
155
+ const override = process.env[envKey]?.trim();
156
+ return override ? override : fallbackModelId;
157
+ }
158
+
159
+ function pickOpenRouterModel(
160
+ models: Model<any>[],
161
+ actualModelId: string,
162
+ fallbackModelId: string,
163
+ displayName: string,
164
+ ): Model<any> | undefined {
165
+ if (models.length === 0) return undefined;
166
+
167
+ const exact = models.find((model) => model.id.toLowerCase() === actualModelId.toLowerCase());
168
+ if (exact) {
169
+ return exact;
170
+ }
171
+
172
+ const fallback =
173
+ models.find((model) => model.id.toLowerCase() === fallbackModelId.toLowerCase()) ?? models[0];
174
+ return {
175
+ ...fallback,
176
+ id: actualModelId,
177
+ name: displayName,
178
+ };
179
+ }
180
+
181
+ function buildDefaultNamingChoice(model: Model<any>): NamingModelChoice {
182
+ return {
183
+ label: `${model.provider}/${model.id}`,
184
+ config: {
185
+ provider: model.provider,
186
+ modelId: model.id,
187
+ },
188
+ };
189
+ }
190
+
191
+ function buildDuckMindNamingChoices(availableModels: Model<any>[]): NamingModelChoice[] {
192
+ const openrouterModels = availableModels.filter((model) => model.provider === OPENROUTER_PROVIDER);
193
+ return DUCKMIND_PRESETS.flatMap((preset) => {
194
+ const actualModelId = resolveDuckMindPresetModelId(preset.envKey, preset.fallbackModelId);
195
+ const model = pickOpenRouterModel(openrouterModels, actualModelId, preset.fallbackModelId, preset.displayName);
196
+ if (!model) {
197
+ return [];
198
+ }
199
+ return [
200
+ {
201
+ label: `${DUCKMIND_PROVIDER}/${preset.alias}`,
202
+ config: {
203
+ provider: DUCKMIND_PROVIDER,
204
+ modelId: preset.alias,
205
+ },
206
+ },
207
+ ];
208
+ });
209
+ }
210
+
211
+ function resolveDuckMindAliasModel(availableModels: Model<any>[], config: NamingModelConfig): Model<any> | undefined {
212
+ const normalizedProvider = config.provider.trim().toLowerCase();
213
+ const normalizedModelId = config.modelId.trim().toLowerCase();
214
+ if (normalizedProvider !== DUCKMIND_PROVIDER && normalizedProvider !== OPENROUTER_PROVIDER) {
215
+ return undefined;
216
+ }
217
+
218
+ const preset = DUCKMIND_PRESETS.find(
219
+ (entry) => normalizedModelId === entry.alias || normalizedModelId === `@preset/${entry.alias}`,
220
+ );
221
+ if (!preset) {
222
+ return undefined;
223
+ }
224
+
225
+ const actualModelId = resolveDuckMindPresetModelId(preset.envKey, preset.fallbackModelId);
226
+ return pickOpenRouterModel(
227
+ availableModels.filter((model) => model.provider === OPENROUTER_PROVIDER),
228
+ actualModelId,
229
+ preset.fallbackModelId,
230
+ preset.displayName,
231
+ );
232
+ }
233
+
105
234
  async function resolveNamingModel(ctx: ExtensionContext, config: NamingModelConfig): Promise<{ model: Model<any>; apiKey: string }> {
106
- const model = ctx.modelRegistry.find(config.provider, config.modelId);
235
+ const model =
236
+ resolveDuckMindAliasModel(ctx.modelRegistry.getAvailable(), config) ??
237
+ ctx.modelRegistry.find(config.provider, config.modelId);
107
238
  if (!model) {
108
239
  throw new Error(`Ultrathink naming model ${config.provider}/${config.modelId} is not available in DM.`);
109
240
  }
@@ -149,14 +280,19 @@ function formatModelOption(model: Model<any>): string {
149
280
  return `${model.provider}/${model.id}`;
150
281
  }
151
282
 
152
- function parseModelOption(value: string): NamingModelConfig {
153
- const slash = value.indexOf("/");
154
- if (slash <= 0 || slash === value.length - 1) {
155
- throw new Error(`Invalid model option: ${value}`);
283
+ function resolveNamingChoice(selection: string, choices: NamingModelChoice[]): NamingModelConfig {
284
+ const choice = choices.find((entry) => entry.label === selection);
285
+ if (choice) {
286
+ return choice.config;
287
+ }
288
+
289
+ const slash = selection.indexOf("/");
290
+ if (slash <= 0 || slash === selection.length - 1) {
291
+ throw new Error(`Invalid model option: ${selection}`);
156
292
  }
157
293
  return {
158
- provider: value.slice(0, slash),
159
- modelId: value.slice(slash + 1),
294
+ provider: selection.slice(0, slash),
295
+ modelId: selection.slice(slash + 1),
160
296
  };
161
297
  }
162
298
 
@@ -181,20 +317,25 @@ export async function ensureNamingModel(
181
317
  .filter((model) => model.input.includes("text"))
182
318
  .sort((a, b) => formatModelOption(a).localeCompare(formatModelOption(b)));
183
319
 
184
- if (availableModels.length === 0) {
320
+ const choices =
321
+ ctx.model?.provider === OPENROUTER_PROVIDER || ctx.model?.provider === DUCKMIND_PROVIDER
322
+ ? buildDuckMindNamingChoices(availableModels)
323
+ : availableModels.map(buildDefaultNamingChoice);
324
+
325
+ if (choices.length === 0) {
185
326
  throw new Error("Ultrathink could not find any available DM models to use for branch and commit naming.");
186
327
  }
187
328
 
188
329
  const selection = await ctx.ui.select(
189
330
  "Choose the small model Ultrathink should use for branch names and commit descriptions",
190
- availableModels.map(formatModelOption),
331
+ choices.map((choice) => choice.label),
191
332
  );
192
333
 
193
334
  if (!selection) {
194
335
  return null;
195
336
  }
196
337
 
197
- const namingModel = parseModelOption(selection);
338
+ const namingModel = resolveNamingChoice(selection, choices);
198
339
  await saveUltrathinkNamingConfig(namingModel);
199
340
  return namingModel;
200
341
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckmind/dm-darwin-x64",
3
- "version": "0.13.2",
3
+ "version": "0.13.6",
4
4
  "description": "DuckMind (dm) binary payload for darwin x64",
5
5
  "license": "MIT",
6
6
  "os": [
@@ -1,72 +1,84 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
2
+ "$schema": "https://duckmind.ai/theme-schema.json",
3
3
  "name": "duckmind",
4
4
  "vars": {
5
- "primary": "#1e90ff",
6
- "secondary": "#808080"
5
+ "deepBlue": "#0a0a10",
6
+ "primaryBg": "#0d0d14",
7
+ "secondaryBg": "#14141f",
8
+ "tertiaryBg": "#1a1a28",
9
+ "elevatedBg": "#202030",
10
+ "hoverBg": "#252535",
11
+ "textPrimary": "#e8e8f0",
12
+ "textSecondary": "#9090a8",
13
+ "textMuted": "#606078",
14
+ "textDim": "#404050",
15
+ "accentPrimary": "#667eea",
16
+ "accentSecondary": "#764ba2",
17
+ "success": "#4ade80",
18
+ "warning": "#fbbf24",
19
+ "error": "#f87171",
20
+ "info": "#60a5fa",
21
+ "indigo": "#818cf8",
22
+ "purple": "#a78bfa",
23
+ "cyan": "#67e8f9",
24
+ "pink": "#f472b6"
7
25
  },
8
26
  "colors": {
9
- "accent": "#1e90ff",
10
- "border": "#1e90ff",
11
- "borderAccent": "#CCCCCC",
12
- "borderMuted": "#808080",
13
-
14
- "success": "#7fd88f",
15
- "error": "#e06c75",
16
- "warning": "#f5a742",
17
-
18
- "muted": "#808080",
19
- "dim": 240,
20
-
21
- "text": "#eeeeee",
22
- "thinkingText": "#808080",
23
-
24
- "selectedBg": "#1a1a1a",
25
- "userMessageBg": "#1a1a1a",
26
- "userMessageText": "#eeeeee",
27
-
28
- "customMessageBg": "#1a1a1a",
29
- "customMessageText": "#eeeeee",
30
- "customMessageLabel": "#1e90ff",
31
-
32
- "toolPendingBg": "#1a1a1a",
33
- "toolSuccessBg": "#1e2e1e",
34
- "toolErrorBg": "#2e1e1e",
35
- "toolTitle": "#1e90ff",
36
- "toolOutput": "#eeeeee",
37
-
38
- "mdHeading": "#56b6c2",
39
- "mdLink": "#1e90ff",
40
- "mdLinkUrl": "#56b6c2",
41
- "mdCode": "#7fd88f",
42
- "mdCodeBlock": "#eeeeee",
43
- "mdCodeBlockBorder": "#808080",
44
- "mdQuote": "#e5c07b",
45
- "mdQuoteBorder": "#808080",
46
- "mdHr": "#808080",
47
- "mdListBullet": "#1e90ff",
48
-
49
- "toolDiffAdded": "#b8db87",
50
- "toolDiffRemoved": "#e26a75",
51
- "toolDiffContext": "#808080",
52
-
53
- "syntaxComment": "#808080",
54
- "syntaxKeyword": "#56b6c2",
55
- "syntaxFunction": "#1e90ff",
56
- "syntaxVariable": "#e06c75",
57
- "syntaxString": "#7fd88f",
58
- "syntaxNumber": "#f5a742",
59
- "syntaxType": "#e5c07b",
60
- "syntaxOperator": "#56b6c2",
61
- "syntaxPunctuation": "#eeeeee",
62
-
63
- "thinkingOff": "#808080",
64
- "thinkingMinimal": "#1e90ff",
65
- "thinkingLow": "#56b6c2",
66
- "thinkingMedium": "#56b6c2",
67
- "thinkingHigh": "#e06c75",
68
- "thinkingXhigh": "#ff0000",
69
-
70
- "bashMode": "#f5a742"
27
+ "accent": "accentPrimary",
28
+ "border": "textDim",
29
+ "borderAccent": "accentPrimary",
30
+ "borderMuted": "textDim",
31
+ "success": "success",
32
+ "error": "error",
33
+ "warning": "warning",
34
+ "muted": "textMuted",
35
+ "dim": "textDim",
36
+ "text": "textPrimary",
37
+ "thinkingText": "textSecondary",
38
+ "selectedBg": "elevatedBg",
39
+ "userMessageBg": "secondaryBg",
40
+ "userMessageText": "textPrimary",
41
+ "customMessageBg": "tertiaryBg",
42
+ "customMessageText": "textPrimary",
43
+ "customMessageLabel": "accentPrimary",
44
+ "toolPendingBg": "primaryBg",
45
+ "toolSuccessBg": "secondaryBg",
46
+ "toolErrorBg": "secondaryBg",
47
+ "toolTitle": "accentPrimary",
48
+ "toolOutput": "textPrimary",
49
+ "mdHeading": "textPrimary",
50
+ "mdLink": "accentPrimary",
51
+ "mdLinkUrl": "textSecondary",
52
+ "mdCode": "cyan",
53
+ "mdCodeBlock": "textPrimary",
54
+ "mdCodeBlockBorder": "textDim",
55
+ "mdQuote": "textSecondary",
56
+ "mdQuoteBorder": "accentPrimary",
57
+ "mdHr": "textDim",
58
+ "mdListBullet": "cyan",
59
+ "toolDiffAdded": "success",
60
+ "toolDiffRemoved": "error",
61
+ "toolDiffContext": "textSecondary",
62
+ "syntaxComment": "textDim",
63
+ "syntaxKeyword": "indigo",
64
+ "syntaxFunction": "accentSecondary",
65
+ "syntaxVariable": "info",
66
+ "syntaxString": "success",
67
+ "syntaxNumber": "warning",
68
+ "syntaxType": "purple",
69
+ "syntaxOperator": "textSecondary",
70
+ "syntaxPunctuation": "textSecondary",
71
+ "thinkingOff": "textDim",
72
+ "thinkingMinimal": "textMuted",
73
+ "thinkingLow": "textSecondary",
74
+ "thinkingMedium": "accentPrimary",
75
+ "thinkingHigh": "accentSecondary",
76
+ "thinkingXhigh": "pink",
77
+ "bashMode": "warning"
78
+ },
79
+ "export": {
80
+ "pageBg": "deepBlue",
81
+ "cardBg": "secondaryBg",
82
+ "infoBg": "tertiaryBg"
71
83
  }
72
84
  }