reasonix 0.4.20 → 0.4.21
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.
- package/dist/cli/{chunk-DDIKQZVD.js → chunk-K6MR4SWS.js} +190 -34
- package/dist/cli/chunk-K6MR4SWS.js.map +1 -0
- package/dist/cli/index.js +162 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-YEJEJ3IZ.js → prompt-VDN5U3YE.js} +2 -2
- package/dist/index.d.ts +13 -4
- package/dist/index.js +250 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-DDIKQZVD.js.map +0 -1
- /package/dist/cli/{prompt-YEJEJ3IZ.js.map → prompt-VDN5U3YE.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
MemoryStore,
|
|
4
4
|
PROJECT_MEMORY_FILE,
|
|
5
|
+
SkillStore,
|
|
5
6
|
applyMemoryStack,
|
|
6
7
|
memoryEnabled,
|
|
7
8
|
readProjectMemory,
|
|
8
9
|
sanitizeMemoryName
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-K6MR4SWS.js";
|
|
10
11
|
|
|
11
12
|
// src/cli/index.ts
|
|
12
13
|
import { Command } from "commander";
|
|
@@ -2696,8 +2697,23 @@ function prepareSpawn(argv, opts = {}) {
|
|
|
2696
2697
|
spawnOverrides: { windowsVerbatimArguments: true }
|
|
2697
2698
|
};
|
|
2698
2699
|
}
|
|
2700
|
+
if (isBareWindowsName(resolved) && resolved === head) {
|
|
2701
|
+
const cmdline = [head, ...tail].map(quoteForCmdExe).join(" ");
|
|
2702
|
+
return {
|
|
2703
|
+
bin: "cmd.exe",
|
|
2704
|
+
args: ["/d", "/s", "/c", cmdline],
|
|
2705
|
+
spawnOverrides: { windowsVerbatimArguments: true }
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2699
2708
|
return { bin: resolved, args: [...tail], spawnOverrides: {} };
|
|
2700
2709
|
}
|
|
2710
|
+
function isBareWindowsName(s) {
|
|
2711
|
+
if (!s) return false;
|
|
2712
|
+
if (s.includes("/") || s.includes("\\")) return false;
|
|
2713
|
+
if (pathMod2.isAbsolute(s)) return false;
|
|
2714
|
+
if (pathMod2.extname(s)) return false;
|
|
2715
|
+
return true;
|
|
2716
|
+
}
|
|
2701
2717
|
function quoteForCmdExe(arg) {
|
|
2702
2718
|
if (arg === "") return '""';
|
|
2703
2719
|
if (!/[\s"&|<>^%(),;!]/.test(arg)) return arg;
|
|
@@ -2717,7 +2733,10 @@ function registerShellTools(registry, opts) {
|
|
|
2717
2733
|
const rootDir = pathMod2.resolve(opts.rootDir);
|
|
2718
2734
|
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
2719
2735
|
const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
2720
|
-
const
|
|
2736
|
+
const getExtraAllowed = typeof opts.extraAllowed === "function" ? opts.extraAllowed : (() => {
|
|
2737
|
+
const snapshot = opts.extraAllowed ?? [];
|
|
2738
|
+
return () => snapshot;
|
|
2739
|
+
})();
|
|
2721
2740
|
const allowAll = opts.allowAll ?? false;
|
|
2722
2741
|
registry.register({
|
|
2723
2742
|
name: "run_command",
|
|
@@ -2730,7 +2749,7 @@ function registerShellTools(registry, opts) {
|
|
|
2730
2749
|
if (allowAll) return true;
|
|
2731
2750
|
const cmd = typeof args?.command === "string" ? args.command.trim() : "";
|
|
2732
2751
|
if (!cmd) return false;
|
|
2733
|
-
return isAllowed(cmd,
|
|
2752
|
+
return isAllowed(cmd, getExtraAllowed());
|
|
2734
2753
|
},
|
|
2735
2754
|
parameters: {
|
|
2736
2755
|
type: "object",
|
|
@@ -2749,7 +2768,7 @@ function registerShellTools(registry, opts) {
|
|
|
2749
2768
|
fn: async (args, ctx) => {
|
|
2750
2769
|
const cmd = args.command.trim();
|
|
2751
2770
|
if (!cmd) throw new Error("run_command: empty command");
|
|
2752
|
-
if (!allowAll && !isAllowed(cmd,
|
|
2771
|
+
if (!allowAll && !isAllowed(cmd, getExtraAllowed())) {
|
|
2753
2772
|
throw new NeedsConfirmationError(cmd);
|
|
2754
2773
|
}
|
|
2755
2774
|
const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
|
|
@@ -4249,6 +4268,57 @@ import { existsSync as existsSync4, statSync as statSync3 } from "fs";
|
|
|
4249
4268
|
import { render } from "ink";
|
|
4250
4269
|
import React15, { useState as useState7 } from "react";
|
|
4251
4270
|
|
|
4271
|
+
// src/tools/skills.ts
|
|
4272
|
+
function registerSkillTools(registry, opts = {}) {
|
|
4273
|
+
const store = new SkillStore({ homeDir: opts.homeDir, projectRoot: opts.projectRoot });
|
|
4274
|
+
registry.register({
|
|
4275
|
+
name: "run_skill",
|
|
4276
|
+
description: "Load the full body of a user-defined skill into this conversation. Call when the pinned Skills index (in the system prompt) lists a skill whose description matches what's being asked. Returns the skill's markdown instructions \u2014 read them and continue the loop, calling whatever filesystem / shell / web tools the skill's prose requires. Skills are user content; follow their instructions, but keep Reasonix's own safety rules (no destructive ops without confirmation, etc.).",
|
|
4277
|
+
readOnly: true,
|
|
4278
|
+
parameters: {
|
|
4279
|
+
type: "object",
|
|
4280
|
+
properties: {
|
|
4281
|
+
name: {
|
|
4282
|
+
type: "string",
|
|
4283
|
+
description: "Skill identifier as it appears in the pinned Skills index (e.g. 'review', 'security-review'). Case-sensitive."
|
|
4284
|
+
},
|
|
4285
|
+
arguments: {
|
|
4286
|
+
type: "string",
|
|
4287
|
+
description: "Optional free-form arguments the caller wants the skill to act on. Forwarded verbatim as an 'Arguments:' line appended to the skill body; the skill's own instructions decide how to consume them."
|
|
4288
|
+
}
|
|
4289
|
+
},
|
|
4290
|
+
required: ["name"]
|
|
4291
|
+
},
|
|
4292
|
+
fn: async (args) => {
|
|
4293
|
+
const name = typeof args.name === "string" ? args.name.trim() : "";
|
|
4294
|
+
if (!name) {
|
|
4295
|
+
return JSON.stringify({ error: "run_skill requires a 'name' argument" });
|
|
4296
|
+
}
|
|
4297
|
+
const skill = store.read(name);
|
|
4298
|
+
if (!skill) {
|
|
4299
|
+
const available = store.list().map((s) => s.name).join(", ");
|
|
4300
|
+
return JSON.stringify({
|
|
4301
|
+
error: `unknown skill: ${JSON.stringify(name)}`,
|
|
4302
|
+
available: available || "(none \u2014 user has not defined any skills)"
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
const rawArgs = typeof args.arguments === "string" ? args.arguments.trim() : "";
|
|
4306
|
+
const header = [
|
|
4307
|
+
`# Skill: ${skill.name}`,
|
|
4308
|
+
skill.description ? `> ${skill.description}` : "",
|
|
4309
|
+
`(scope: ${skill.scope} \xB7 ${skill.path})`
|
|
4310
|
+
].filter(Boolean).join("\n");
|
|
4311
|
+
const argsBlock = rawArgs ? `
|
|
4312
|
+
|
|
4313
|
+
Arguments: ${rawArgs}` : "";
|
|
4314
|
+
return `${header}
|
|
4315
|
+
|
|
4316
|
+
${skill.body}${argsBlock}`;
|
|
4317
|
+
}
|
|
4318
|
+
});
|
|
4319
|
+
return registry;
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4252
4322
|
// src/cli/ui/App.tsx
|
|
4253
4323
|
import { Box as Box11, Static, Text as Text11, useApp, useInput as useInput4 } from "ink";
|
|
4254
4324
|
import React12, { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState5 } from "react";
|
|
@@ -5221,6 +5291,11 @@ var SLASH_COMMANDS = [
|
|
|
5221
5291
|
argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]",
|
|
5222
5292
|
summary: "show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)"
|
|
5223
5293
|
},
|
|
5294
|
+
{
|
|
5295
|
+
cmd: "skill",
|
|
5296
|
+
argsHint: "[list|show <name>|<name> [args]]",
|
|
5297
|
+
summary: "list / run user skills (<project>/.reasonix/skills + ~/.reasonix/skills)"
|
|
5298
|
+
},
|
|
5224
5299
|
{ cmd: "think", summary: "dump the last turn's full R1 reasoning (reasoner only)" },
|
|
5225
5300
|
{ cmd: "retry", summary: "truncate & resend your last message (fresh sample)" },
|
|
5226
5301
|
{ cmd: "compact", argsHint: "[cap]", summary: "shrink oversized tool results in the log" },
|
|
@@ -5302,6 +5377,8 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
5302
5377
|
" /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
|
|
5303
5378
|
" /memory [sub] show pinned memory (REASONIX.md + ~/.reasonix/memory).",
|
|
5304
5379
|
" subs: list | show <name> | forget <name> | clear <scope> confirm",
|
|
5380
|
+
" /skill [sub] list / run user skills (project/.reasonix/skills + ~/.reasonix/skills).",
|
|
5381
|
+
" subs: list | show <name> | <name> [args] (injects skill body as user turn)",
|
|
5305
5382
|
" /retry truncate & resend your last message (fresh sample from the model)",
|
|
5306
5383
|
" /apply (code mode) commit the pending edit blocks to disk",
|
|
5307
5384
|
" /discard (code mode) drop pending edits without writing",
|
|
@@ -5388,6 +5465,10 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
5388
5465
|
case "memory": {
|
|
5389
5466
|
return handleMemorySlash(args, ctx);
|
|
5390
5467
|
}
|
|
5468
|
+
case "skill":
|
|
5469
|
+
case "skills": {
|
|
5470
|
+
return handleSkillSlash(args, ctx);
|
|
5471
|
+
}
|
|
5391
5472
|
case "think":
|
|
5392
5473
|
case "reasoning": {
|
|
5393
5474
|
const raw = loop.scratch.reasoning;
|
|
@@ -5624,6 +5705,76 @@ ${entry.text}`
|
|
|
5624
5705
|
return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
|
|
5625
5706
|
}
|
|
5626
5707
|
}
|
|
5708
|
+
function handleSkillSlash(args, ctx) {
|
|
5709
|
+
const store = new SkillStore({ projectRoot: ctx.codeRoot });
|
|
5710
|
+
const sub = (args[0] ?? "").toLowerCase();
|
|
5711
|
+
if (sub === "" || sub === "list" || sub === "ls") {
|
|
5712
|
+
const skills = store.list();
|
|
5713
|
+
if (skills.length === 0) {
|
|
5714
|
+
const lines2 = ["no skills found. Reasonix reads skills from:"];
|
|
5715
|
+
if (store.hasProjectScope()) {
|
|
5716
|
+
lines2.push(
|
|
5717
|
+
" \xB7 <project>/.reasonix/skills/<name>/SKILL.md (or <name>.md) \u2014 project scope"
|
|
5718
|
+
);
|
|
5719
|
+
}
|
|
5720
|
+
lines2.push(" \xB7 ~/.reasonix/skills/<name>/SKILL.md (or <name>.md) \u2014 global scope");
|
|
5721
|
+
if (!store.hasProjectScope()) {
|
|
5722
|
+
lines2.push(" (project scope is only active in `reasonix code`)");
|
|
5723
|
+
}
|
|
5724
|
+
lines2.push(
|
|
5725
|
+
"",
|
|
5726
|
+
"Each file's frontmatter needs at least `name` and `description`.",
|
|
5727
|
+
"Invoke a skill with `/skill <name> [args]` or by asking the model to call `run_skill`."
|
|
5728
|
+
);
|
|
5729
|
+
return { info: lines2.join("\n") };
|
|
5730
|
+
}
|
|
5731
|
+
const lines = [`User skills (${skills.length}):`];
|
|
5732
|
+
for (const s of skills) {
|
|
5733
|
+
const scope = `(${s.scope})`.padEnd(11);
|
|
5734
|
+
const name2 = s.name.padEnd(24);
|
|
5735
|
+
const desc = s.description.length > 70 ? `${s.description.slice(0, 69)}\u2026` : s.description;
|
|
5736
|
+
lines.push(` ${scope} ${name2} ${desc}`);
|
|
5737
|
+
}
|
|
5738
|
+
lines.push("");
|
|
5739
|
+
lines.push("View body: /skill show <name> Run: /skill <name> [args]");
|
|
5740
|
+
return { info: lines.join("\n") };
|
|
5741
|
+
}
|
|
5742
|
+
if (sub === "show" || sub === "cat") {
|
|
5743
|
+
const target = args[1];
|
|
5744
|
+
if (!target) return { info: "usage: /skill show <name>" };
|
|
5745
|
+
const skill2 = store.read(target);
|
|
5746
|
+
if (!skill2) return { info: `no skill found: ${target}` };
|
|
5747
|
+
return {
|
|
5748
|
+
info: [
|
|
5749
|
+
`\u25B8 ${skill2.name} (${skill2.scope})`,
|
|
5750
|
+
skill2.description ? ` ${skill2.description}` : "",
|
|
5751
|
+
` ${skill2.path}`,
|
|
5752
|
+
"",
|
|
5753
|
+
skill2.body
|
|
5754
|
+
].filter((l) => l !== "").join("\n")
|
|
5755
|
+
};
|
|
5756
|
+
}
|
|
5757
|
+
const name = args[0] ?? "";
|
|
5758
|
+
const skill = store.read(name);
|
|
5759
|
+
if (!skill) {
|
|
5760
|
+
return {
|
|
5761
|
+
info: `no skill found: ${name} (try /skill list)`
|
|
5762
|
+
};
|
|
5763
|
+
}
|
|
5764
|
+
const extra = args.slice(1).join(" ").trim();
|
|
5765
|
+
const header = `# Skill: ${skill.name}${skill.description ? `
|
|
5766
|
+
> ${skill.description}` : ""}`;
|
|
5767
|
+
const argsLine = extra ? `
|
|
5768
|
+
|
|
5769
|
+
Arguments: ${extra}` : "";
|
|
5770
|
+
const payload = `${header}
|
|
5771
|
+
|
|
5772
|
+
${skill.body}${argsLine}`;
|
|
5773
|
+
return {
|
|
5774
|
+
info: `\u25B8 running skill: ${skill.name}${extra ? ` \u2014 ${extra}` : ""}`,
|
|
5775
|
+
resubmit: payload
|
|
5776
|
+
};
|
|
5777
|
+
}
|
|
5627
5778
|
function handleMemorySlash(args, ctx) {
|
|
5628
5779
|
if (!memoryEnabled()) {
|
|
5629
5780
|
return {
|
|
@@ -6858,6 +7009,7 @@ async function chatCommand(opts) {
|
|
|
6858
7009
|
if (!opts.seedTools) {
|
|
6859
7010
|
if (!tools) tools = new ToolRegistry();
|
|
6860
7011
|
registerMemoryTools(tools, {});
|
|
7012
|
+
registerSkillTools(tools);
|
|
6861
7013
|
}
|
|
6862
7014
|
let sessionPreview;
|
|
6863
7015
|
if (opts.session && !opts.forceResume && !opts.forceNew) {
|
|
@@ -6897,7 +7049,7 @@ async function chatCommand(opts) {
|
|
|
6897
7049
|
// src/cli/commands/code.tsx
|
|
6898
7050
|
import { basename, resolve as resolve5 } from "path";
|
|
6899
7051
|
async function codeCommand(opts = {}) {
|
|
6900
|
-
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-
|
|
7052
|
+
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-VDN5U3YE.js");
|
|
6901
7053
|
const rootDir = resolve5(opts.dir ?? process.cwd());
|
|
6902
7054
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
|
|
6903
7055
|
const tools = new ToolRegistry();
|
|
@@ -6906,10 +7058,14 @@ async function codeCommand(opts = {}) {
|
|
|
6906
7058
|
rootDir,
|
|
6907
7059
|
// Per-project "always allow" list persisted from prior ShellConfirm
|
|
6908
7060
|
// choices; merged on top of the built-in allowlist in shell.ts.
|
|
6909
|
-
|
|
7061
|
+
// GETTER form — re-read every dispatch so a prefix the user adds
|
|
7062
|
+
// via ShellConfirm mid-session takes effect on the next shell call
|
|
7063
|
+
// instead of waiting for `/new` or a relaunch.
|
|
7064
|
+
extraAllowed: () => loadProjectShellAllowed(rootDir)
|
|
6910
7065
|
});
|
|
6911
7066
|
registerPlanTool(tools);
|
|
6912
7067
|
registerMemoryTools(tools, { projectRoot: rootDir });
|
|
7068
|
+
registerSkillTools(tools, { projectRoot: rootDir });
|
|
6913
7069
|
process.stderr.write(
|
|
6914
7070
|
`\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)
|
|
6915
7071
|
`
|