@duckmind/dm-windows-x64 0.63.4 → 0.63.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 (32) hide show
  1. package/extensions/.dm-extensions.json +64 -4
  2. package/extensions/dm-context/package.json +1 -1
  3. package/extensions/dm-context/skills/context-management/SKILL.md +173 -223
  4. package/extensions/dm-context/skills/context-management/references/development-and-troubleshooting.md +75 -0
  5. package/extensions/dm-context/skills/context-management/references/interleaved-async-work.md +143 -0
  6. package/extensions/dm-context/skills/context-management/references/planning-and-execution.md +81 -0
  7. package/extensions/dm-context/skills/context-management/references/repeated-items-and-batch-work.md +69 -0
  8. package/extensions/dm-context/skills/context-management/references/retry-branch-and-pivot.md +80 -0
  9. package/extensions/dm-context/skills/context-management/references/search-research-and-reading.md +103 -0
  10. package/extensions/dm-context/skills/context-management/references/task-switching-and-cleanup.md +73 -0
  11. package/extensions/dm-context/src/context.js +3 -2
  12. package/extensions/dm-context/src/index.js +168 -84
  13. package/extensions/dm-skills-manager/THIRD_PARTY_NOTICES.md +27 -0
  14. package/extensions/dm-skills-manager/extensions/skills-manager/components.js +265 -0
  15. package/extensions/dm-skills-manager/extensions/skills-manager/constants.js +31 -0
  16. package/extensions/dm-skills-manager/extensions/skills-manager/creation-fallback.js +20 -0
  17. package/extensions/dm-skills-manager/extensions/skills-manager/creation.js +145 -0
  18. package/extensions/dm-skills-manager/extensions/skills-manager/dialog.js +738 -0
  19. package/extensions/dm-skills-manager/extensions/skills-manager/dm-ai-compat.js +15 -0
  20. package/extensions/dm-skills-manager/extensions/skills-manager/format.js +125 -0
  21. package/extensions/dm-skills-manager/extensions/skills-manager/glyphs.js +142 -0
  22. package/extensions/dm-skills-manager/extensions/skills-manager/layout.js +93 -0
  23. package/extensions/dm-skills-manager/extensions/skills-manager/paths.js +76 -0
  24. package/extensions/dm-skills-manager/extensions/skills-manager/registry.js +95 -0
  25. package/extensions/dm-skills-manager/extensions/skills-manager/settings.js +93 -0
  26. package/extensions/dm-skills-manager/extensions/skills-manager/startup.js +32 -0
  27. package/extensions/dm-skills-manager/extensions/skills-manager/toggle.js +54 -0
  28. package/extensions/dm-skills-manager/extensions/skills-manager/types.js +14 -0
  29. package/extensions/dm-skills-manager/extensions/skills-manager/ui.js +121 -0
  30. package/extensions/dm-skills-manager/extensions/skills-manager.js +111 -0
  31. package/extensions/dm-skills-manager/package.json +121 -0
  32. package/package.json +1 -1
@@ -0,0 +1,54 @@
1
+ import { relative, dirname } from "node:path";
2
+ import { getAgentDir, SettingsManager } from "@duckmind/dm-coding-agent";
3
+ import { findProjectDmDir, projectSettingsTrusted } from "./paths.js";
4
+ function updatePatterns(current, pattern, enabled) {
5
+ const updated = current.filter((entry) => {
6
+ const stripped = entry.startsWith("!") || entry.startsWith("+") || entry.startsWith("-") ? entry.slice(1) : entry;
7
+ return stripped !== pattern;
8
+ });
9
+ updated.push(`${enabled ? "+" : "-"}${pattern}`);
10
+ return updated;
11
+ }
12
+ function getTopLevelPattern(skill, cwd) {
13
+ const baseDir = skill.scope === "project" ? findProjectDmDir(cwd) : getAgentDir();
14
+ return relative(baseDir, skill.path);
15
+ }
16
+ function getPackagePattern(skill) {
17
+ const baseDir = skill.baseDir ?? dirname(skill.path);
18
+ return relative(baseDir, skill.path);
19
+ }
20
+ function hasPackageFilters(pkg) {
21
+ return pkg.extensions !== undefined || pkg.skills !== undefined || pkg.prompts !== undefined || pkg.themes !== undefined;
22
+ }
23
+ export async function setSkillEnabled(cwd, skill, enabled) {
24
+ if (skill.scope === "temporary")
25
+ throw new Error("Temporary skills cannot be toggled.");
26
+ if (skill.scope === "project" && !projectSettingsTrusted(cwd))
27
+ throw new Error("Project skills cannot be toggled before project trust is active.");
28
+ const settingsManager = SettingsManager.create(cwd, getAgentDir(), { projectTrusted: projectSettingsTrusted(cwd) });
29
+ if (skill.origin === "top-level") {
30
+ const settings = skill.scope === "project" ? settingsManager.getProjectSettings() : settingsManager.getGlobalSettings();
31
+ const updated = updatePatterns([...settings.skills ?? []], getTopLevelPattern(skill, cwd), enabled);
32
+ if (skill.scope === "project")
33
+ settingsManager.setProjectSkillPaths(updated);
34
+ else
35
+ settingsManager.setSkillPaths(updated);
36
+ await settingsManager.flush();
37
+ return;
38
+ }
39
+ const settings = skill.scope === "project" ? settingsManager.getProjectSettings() : settingsManager.getGlobalSettings();
40
+ const packages = [...settings.packages ?? []];
41
+ const packageIndex = packages.findIndex((pkg) => (typeof pkg === "string" ? pkg : pkg.source) === skill.source);
42
+ if (packageIndex === -1)
43
+ throw new Error("Could not find the package settings entry for this skill.");
44
+ const packageEntry = packages[packageIndex];
45
+ const packageConfig = typeof packageEntry === "string" ? { source: packageEntry } : { ...packageEntry };
46
+ const updated = updatePatterns([...packageConfig.skills ?? []], getPackagePattern(skill), enabled);
47
+ packageConfig.skills = updated.length > 0 ? updated : undefined;
48
+ packages[packageIndex] = hasPackageFilters(packageConfig) ? packageConfig : packageConfig.source;
49
+ if (skill.scope === "project")
50
+ settingsManager.setProjectPackages(packages);
51
+ else
52
+ settingsManager.setPackages(packages);
53
+ await settingsManager.flush();
54
+ }
@@ -0,0 +1,14 @@
1
+ export const EMPTY_REGISTRY = {
2
+ skills: [],
3
+ allSkills: [],
4
+ byName: new Map
5
+ };
6
+ export const LOCATION_OPTIONS = [
7
+ { value: "project", label: "Project", description: "Save in this project's .dm/skills directory." },
8
+ { value: "global", label: "Global", description: "Save in your user-level DM skills directory." }
9
+ ];
10
+ export const CREATE_STEPS = [
11
+ { id: "name", title: "Name", hint: "Use lowercase letters, numbers, and hyphens, for example react-review.", optional: false, kind: "text" },
12
+ { id: "description", title: "Description", hint: "Describe what the skill does and when it should be used. Specific trigger language works best.", optional: false, kind: "text" },
13
+ { id: "location", title: "Visibility", hint: "Choose whether the skill is project-local or available in all DM sessions.", optional: false, kind: "choice", options: LOCATION_OPTIONS }
14
+ ];
@@ -0,0 +1,121 @@
1
+ import { truncateToWidth, visibleWidth } from "@duckmind/dm-tui";
2
+ import { ansiGreen, ansiYellow, KENDEX_MODAL_LOCK_SYMBOL } from "./constants.js";
3
+ import { frameGlyphs, glyphs } from "./glyphs.js";
4
+ export function inlineLine(text) {
5
+ return text.replace(/[\r\n]+/g, " ").replace(/\t/g, " ");
6
+ }
7
+ export function acquirekendexModalLock() {
8
+ const host = globalThis;
9
+ const existing = host[KENDEX_MODAL_LOCK_SYMBOL];
10
+ const lock = existing && typeof existing.depth === "number" ? existing : { depth: 0 };
11
+ host[KENDEX_MODAL_LOCK_SYMBOL] = lock;
12
+ lock.depth += 1;
13
+ let released = false;
14
+ return () => {
15
+ if (released)
16
+ return;
17
+ released = true;
18
+ lock.depth = Math.max(0, lock.depth - 1);
19
+ };
20
+ }
21
+ export function padAnsi(text, width) {
22
+ const safeWidth = Math.max(0, width);
23
+ const truncated = truncateToWidth(inlineLine(text), safeWidth, "");
24
+ return `${truncated}${" ".repeat(Math.max(0, safeWidth - visibleWidth(truncated)))}`;
25
+ }
26
+ export function toneText(theme, tone, text) {
27
+ if (tone === "error")
28
+ return theme.fg("error", text);
29
+ if (tone === "success")
30
+ return theme.fg("success", text);
31
+ return theme.fg("dim", text);
32
+ }
33
+ export function skillEntityTitle(theme, label) {
34
+ return theme.fg("text", theme.bold(label));
35
+ }
36
+ export function skillSectionTitle(theme, label) {
37
+ return theme.fg("muted", theme.bold(label));
38
+ }
39
+ export function skillSelectedLine(theme, line, width) {
40
+ return theme.bg("selectedBg", padAnsi(line, width));
41
+ }
42
+ export function skillKeyHints(theme, hints) {
43
+ return hints.map(([key, description]) => `${ansiYellow(key)} ${theme.fg("dim", description)}`).join(theme.fg("dim", ` ${glyphs().bullet.trim()} `));
44
+ }
45
+ function frameLine(theme, line, innerWidth) {
46
+ const clipped = truncateToWidth(inlineLine(line), innerWidth, theme.fg("dim", "..."));
47
+ const frame = frameGlyphs();
48
+ return `${theme.fg("borderAccent", `${frame.v} `)}${padAnsi(clipped, innerWidth)}${theme.fg("borderAccent", ` ${frame.v}`)}`;
49
+ }
50
+ function fitFrameBody(theme, lines, fixedInnerRows) {
51
+ if (fixedInnerRows === undefined)
52
+ return lines;
53
+ const rowCount = Math.max(1, Math.floor(fixedInnerRows));
54
+ if (lines.length > rowCount) {
55
+ const hidden = lines.length - rowCount + 1;
56
+ return [...lines.slice(0, Math.max(0, rowCount - 1)), theme.fg("dim", `↓ ${hidden} more line(s)`)].slice(0, rowCount);
57
+ }
58
+ if (lines.length < rowCount)
59
+ return [...lines, ...Array.from({ length: rowCount - lines.length }, () => "")];
60
+ return lines;
61
+ }
62
+ export function renderFrame(theme, width, lines, fixedInnerRows, title = "", right = "") {
63
+ const body = fitFrameBody(theme, lines, fixedInnerRows);
64
+ if (width < 6)
65
+ return body.map((line) => truncateToWidth(inlineLine(line), width, ""));
66
+ const innerWidth = Math.max(1, width - 4);
67
+ const frame = frameGlyphs();
68
+ const top = () => {
69
+ const contentWidth = innerWidth + 2;
70
+ if (!title && !right)
71
+ return theme.fg("borderAccent", `${frame.tl}${frame.h.repeat(contentWidth)}${frame.tr}`);
72
+ const rightPlain = right ? ` ${right} ` : "";
73
+ const titleBudget = Math.max(1, contentWidth - visibleWidth(rightPlain) - 1);
74
+ const titlePlain = title ? ` ${truncateToWidth(title, Math.max(1, titleBudget - 2), glyphs().ellipsis)} ` : "";
75
+ const fill = Math.max(1, contentWidth - visibleWidth(titlePlain) - visibleWidth(rightPlain));
76
+ return `${theme.fg("borderAccent", frame.tl)}${titlePlain ? ansiGreen(titlePlain) : ""}${theme.fg("borderAccent", frame.h.repeat(fill))}${rightPlain ? theme.fg("dim", rightPlain) : ""}${theme.fg("borderAccent", frame.tr)}`;
77
+ };
78
+ return [
79
+ top(),
80
+ ...body.map((line) => frameLine(theme, line, innerWidth)),
81
+ theme.fg("borderAccent", `${frame.bl}${frame.h.repeat(innerWidth + 2)}${frame.br}`)
82
+ ].map((line) => truncateToWidth(inlineLine(line), width, ""));
83
+ }
84
+ function centerLines(lines, width) {
85
+ const renderedWidth = lines.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
86
+ const leftPad = Math.max(0, Math.floor((width - renderedWidth) / 2));
87
+ return leftPad === 0 ? lines : lines.map((line) => `${" ".repeat(leftPad)}${line}`);
88
+ }
89
+ export function renderCenteredDialog(theme, width, lines, maxInnerWidth = 68) {
90
+ if (width < 8)
91
+ return lines.map((line) => truncateToWidth(line, width, ""));
92
+ const innerWidth = Math.max(1, Math.min(width - 4, maxInnerWidth));
93
+ const frame = frameGlyphs();
94
+ const framed = [
95
+ theme.fg("borderAccent", `${frame.tl}${frame.h.repeat(innerWidth + 2)}${frame.tr}`),
96
+ ...lines.map((line) => frameLine(theme, line, innerWidth)),
97
+ theme.fg("borderAccent", `${frame.bl}${frame.h.repeat(innerWidth + 2)}${frame.br}`)
98
+ ];
99
+ return centerLines(framed, width);
100
+ }
101
+ export function getEditorTheme(theme) {
102
+ return {
103
+ borderColor: (text) => theme.fg("accent", text),
104
+ selectList: {
105
+ selectedPrefix: (text) => theme.fg("accent", text),
106
+ selectedText: (text) => theme.bg("selectedBg", theme.fg("text", text)),
107
+ description: (text) => theme.fg("muted", text),
108
+ scrollInfo: (text) => theme.fg("dim", text),
109
+ noMatch: (text) => theme.fg("warning", text)
110
+ }
111
+ };
112
+ }
113
+ export function scopeLabel(skill) {
114
+ return skill.scope === "project" ? "project" : skill.scope === "user" ? "global" : "temporary";
115
+ }
116
+ export function packageLabel(skill) {
117
+ return skill.origin === "package" && skill.source ? skill.source : undefined;
118
+ }
119
+ export function skillLocation(skill) {
120
+ return skill.origin === "package" ? skill.source : skill.path;
121
+ }
@@ -0,0 +1,111 @@
1
+ import { INSTALL_SYMBOL } from "./skills-manager/constants.js";
2
+ import { createSkillFromAnswers } from "./skills-manager/creation.js";
3
+ import { showSkillsManager } from "./skills-manager/dialog.js";
4
+ import { recordProjectTrust } from "./skills-manager/paths.js";
5
+ import { deleteSkill, loadSkillRegistry } from "./skills-manager/registry.js";
6
+ import { settingBoolean, updatePackageConfig } from "./skills-manager/settings.js";
7
+ import { patchInteractiveModeStartupSkillsBlock, setStartupHideEnabled } from "./skills-manager/startup.js";
8
+ import { setSkillEnabled } from "./skills-manager/toggle.js";
9
+ import { EMPTY_REGISTRY } from "./skills-manager/types.js";
10
+ function errorMessage(error) {
11
+ const message = error instanceof Error ? error.message : String(error);
12
+ return message.length > 180 ? `${message.slice(0, 179)}…` : message;
13
+ }
14
+ function insertNativeSkillCommand(ctx, skill) {
15
+ ctx.ui.pasteToEditor(`/skill:${skill.name}
16
+ `);
17
+ }
18
+ function dmThinkingLevel(value) {
19
+ if (value === "max")
20
+ return "xhigh";
21
+ return value === "off" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" ? value : "off";
22
+ }
23
+ export default function skillsManager(pi) {
24
+ const guard = pi;
25
+ if (guard[INSTALL_SYMBOL])
26
+ return;
27
+ guard[INSTALL_SYMBOL] = true;
28
+ patchInteractiveModeStartupSkillsBlock();
29
+ setStartupHideEnabled(settingBoolean("enabled", true) && settingBoolean("hideStartupSkillsBlock", true));
30
+ let registry = EMPTY_REGISTRY;
31
+ const enabledAtLoad = settingBoolean("enabled", true);
32
+ if (!enabledAtLoad) {
33
+ const enableRecovery = async (ctx) => {
34
+ updatePackageConfig(ctx.cwd, { enabled: true });
35
+ ctx.ui.notify("Skills Manager enabled. Reloading...", "info");
36
+ await ctx.reload();
37
+ };
38
+ pi.registerCommand("skill", {
39
+ description: "Skills manager recovery command.",
40
+ handler: async (args, ctx) => {
41
+ if (args.trim().toLowerCase() !== "enable") {
42
+ ctx.ui.notify("Skills Manager is disabled. Run /skill:enable, then /reload.", "warning");
43
+ return;
44
+ }
45
+ await enableRecovery(ctx);
46
+ }
47
+ });
48
+ pi.registerCommand("skill:enable", {
49
+ description: "Re-enable the skills manager",
50
+ handler: async (_args, ctx) => enableRecovery(ctx)
51
+ });
52
+ return;
53
+ }
54
+ async function refreshRegistry(cwd) {
55
+ registry = await loadSkillRegistry(cwd);
56
+ return registry;
57
+ }
58
+ async function prepareSession(ctx) {
59
+ recordProjectTrust(ctx);
60
+ setStartupHideEnabled(settingBoolean("enabled", true, ctx.cwd) && settingBoolean("hideStartupSkillsBlock", true, ctx.cwd));
61
+ try {
62
+ await refreshRegistry(ctx.cwd);
63
+ } catch (error) {
64
+ registry = EMPTY_REGISTRY;
65
+ ctx.ui.notify(`Skills Manager failed to load registry: ${errorMessage(error)}`, "error");
66
+ }
67
+ return false;
68
+ }
69
+ pi.registerCommand("skill", {
70
+ description: "DM skills manager view. Native skills remain /skill:name.",
71
+ handler: async (args, ctx) => {
72
+ const rawArgs = args.trim();
73
+ const trimmed = rawArgs.toLowerCase();
74
+ if (trimmed === "enable") {
75
+ updatePackageConfig(ctx.cwd, { enabled: true });
76
+ ctx.ui.notify("Skills Manager already enabled.", "info");
77
+ return;
78
+ }
79
+ if (trimmed === "disable") {
80
+ updatePackageConfig(ctx.cwd, { enabled: false });
81
+ ctx.ui.notify("Skills Manager disabled. Run /reload to unload commands/hooks.", "info");
82
+ return;
83
+ }
84
+ if (rawArgs) {
85
+ ctx.ui.notify("Use /skill:name for native skill invocation, or /skill with no arguments for the manager.", "warning");
86
+ return;
87
+ }
88
+ if (!ctx.hasUI) {
89
+ ctx.ui.notify("/skill manager requires interactive mode", "warning");
90
+ return;
91
+ }
92
+ try {
93
+ await refreshRegistry(ctx.cwd);
94
+ } catch (error) {
95
+ ctx.ui.notify(`Failed to load skills list: ${errorMessage(error)}`, "error");
96
+ return;
97
+ }
98
+ const selection = await showSkillsManager(ctx, registry, {
99
+ onCreate: async (answers, signal) => await createSkillFromAnswers(ctx, answers, { thinkingLevel: dmThinkingLevel(pi.getThinkingLevel()), signal }),
100
+ onDelete: async (skill) => await deleteSkill(ctx, skill),
101
+ onToggle: async (skill, enabled) => await setSkillEnabled(ctx.cwd, skill, enabled),
102
+ onRefresh: async () => await refreshRegistry(ctx.cwd)
103
+ });
104
+ if (selection)
105
+ insertNativeSkillCommand(ctx, selection);
106
+ }
107
+ });
108
+ pi.on("session_start", async (_event, ctx) => {
109
+ await prepareSession(ctx);
110
+ });
111
+ }
@@ -0,0 +1,121 @@
1
+ {
2
+ "name": "dm-skills-manager",
3
+ "version": "2.0.0",
4
+ "description": "Skills manager for DM with browsing, preview, insertion, creation, editing, renaming, deletion, and enablement controls.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "duckmind",
9
+ "dm",
10
+ "dm-package",
11
+ "dm-extension",
12
+ "skills",
13
+ "manager"
14
+ ],
15
+ "dm": {
16
+ "extensions": [
17
+ "./extensions/skills-manager.js"
18
+ ],
19
+ "image": "https://raw.githubusercontent.com/vanillagreencom/kendex/9e4bc7e8ffa832e8d86b6e849cff1a108a679225/pi-extensions/pi-skills-manager/assets/skills-manager.png"
20
+ },
21
+ "kendex": {
22
+ "extensionManager": {
23
+ "displayName": "Skills Manager",
24
+ "settings": [
25
+ {
26
+ "key": "enabled",
27
+ "label": "Enable skills manager",
28
+ "description": "Register the /skill manager view, startup skill-list hiding, and skills management UI. If disabled, /skill enable remains available as a recovery command. Native /skill:name commands stay controlled by DM's Skill commands setting.",
29
+ "type": "boolean",
30
+ "default": true,
31
+ "category": "General",
32
+ "apply": "reload",
33
+ "requiresReload": true
34
+ },
35
+ {
36
+ "key": "glyphStyle",
37
+ "label": "Glyph style",
38
+ "description": "Glyphs for kendex-owned UI chrome. Unicode is the default; ASCII uses plain box/status/separator characters for terminal compatibility. DM tool renderer's global override wins when set.",
39
+ "type": "enum",
40
+ "enumValues": [
41
+ "unicode",
42
+ "ascii"
43
+ ],
44
+ "default": "unicode",
45
+ "category": "Rendering",
46
+ "apply": "live"
47
+ },
48
+ {
49
+ "key": "hideStartupSkillsBlock",
50
+ "label": "Hide startup skills block",
51
+ "description": "Hide DM's built-in startup [Skills] list so skills are managed through the dedicated menu instead.",
52
+ "type": "boolean",
53
+ "default": true,
54
+ "category": "Integration",
55
+ "apply": "reload",
56
+ "requiresReload": true
57
+ },
58
+ {
59
+ "key": "aiGenerationEnabled",
60
+ "label": "AI skill generation",
61
+ "description": "Use the currently selected model/thinking level to draft new SKILL.md files. If unavailable or disabled, a deterministic fallback template is used.",
62
+ "type": "boolean",
63
+ "default": true,
64
+ "category": "Creation",
65
+ "apply": "live"
66
+ },
67
+ {
68
+ "key": "defaultCreateLocation",
69
+ "label": "Default create location",
70
+ "description": "Initial visibility choice when creating a skill.",
71
+ "type": "enum",
72
+ "enumValues": [
73
+ "project",
74
+ "global"
75
+ ],
76
+ "default": "project",
77
+ "category": "Creation",
78
+ "apply": "live"
79
+ },
80
+ {
81
+ "key": "popupWidth",
82
+ "label": "Popup width",
83
+ "description": "Preferred overlay width. Accepts a number of columns or a percentage string such as 82%.",
84
+ "type": "string",
85
+ "default": "82%",
86
+ "category": "UI",
87
+ "apply": "live"
88
+ },
89
+ {
90
+ "key": "popupMaxHeight",
91
+ "label": "Popup max height",
92
+ "description": "Maximum overlay height. Accepts a number of rows or a percentage string.",
93
+ "type": "string",
94
+ "default": "86%",
95
+ "category": "UI",
96
+ "apply": "live"
97
+ },
98
+ {
99
+ "key": "listRows",
100
+ "label": "Visible list rows",
101
+ "description": "Maximum list rows shown before scrolling in browse mode. Short terminals shrink this to keep popup controls visible.",
102
+ "type": "number",
103
+ "default": 14,
104
+ "category": "UI",
105
+ "apply": "live"
106
+ }
107
+ ]
108
+ }
109
+ },
110
+ "peerDependenciesMeta": {
111
+ "@duckmind/dm-ai": {
112
+ "optional": true
113
+ },
114
+ "@duckmind/dm-coding-agent": {
115
+ "optional": true
116
+ },
117
+ "@duckmind/dm-tui": {
118
+ "optional": true
119
+ }
120
+ }
121
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckmind/dm-windows-x64",
3
- "version": "0.63.4",
3
+ "version": "0.63.6",
4
4
  "description": "DuckMind (dm) binary payload for windows x64",
5
5
  "license": "MIT",
6
6
  "os": [