@docyrus/docyrus 0.0.39 → 0.0.41
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/README.md +5 -0
- package/main.js +1983 -486
- package/main.js.map +4 -4
- package/package.json +4 -1
- package/resources/officecli/install.mjs +111 -0
- package/resources/officecli/manifest.json +3 -0
- package/resources/pi-agent/extensions/plan.ts +95 -0
- package/resources/pi-agent/extensions/tasks.ts +497 -0
- package/resources/pi-agent/extensions/todos.ts +102 -2
- package/resources/pi-agent/skills/officecli/SKILL.md +2 -1
- package/server-loader.js +24187 -3349
- package/server-loader.js.map +4 -4
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docyrus/docyrus",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Docyrus API CLI",
|
|
6
6
|
"main": "./main.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"docyrus": "main.js"
|
|
9
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node ./resources/officecli/install.mjs"
|
|
12
|
+
},
|
|
10
13
|
"dependencies": {
|
|
11
14
|
"@clack/prompts": "^0.11.0",
|
|
12
15
|
"@hono/node-server": "^1.14.1",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const SKIP_ENV_NAME = "DOCYRUS_SKIP_OFFICECLI_POSTINSTALL";
|
|
7
|
+
const scriptDirectoryPath = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRootPath = resolve(scriptDirectoryPath, "..", "..");
|
|
9
|
+
const manifestPath = join(scriptDirectoryPath, "manifest.json");
|
|
10
|
+
const bundledRootPath = join(scriptDirectoryPath, "bundled");
|
|
11
|
+
|
|
12
|
+
function resolveOfficeCliAssetName() {
|
|
13
|
+
switch (process.platform) {
|
|
14
|
+
case "darwin":
|
|
15
|
+
if (process.arch === "arm64") {
|
|
16
|
+
return "officecli-mac-arm64";
|
|
17
|
+
}
|
|
18
|
+
if (process.arch === "x64") {
|
|
19
|
+
return "officecli-mac-x64";
|
|
20
|
+
}
|
|
21
|
+
break;
|
|
22
|
+
case "linux": {
|
|
23
|
+
const alpineSuffix = existsSync("/etc/alpine-release") ? "-alpine" : "";
|
|
24
|
+
if (process.arch === "arm64") {
|
|
25
|
+
return `officecli-linux${alpineSuffix}-arm64`;
|
|
26
|
+
}
|
|
27
|
+
if (process.arch === "x64") {
|
|
28
|
+
return `officecli-linux${alpineSuffix}-x64`;
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case "win32":
|
|
33
|
+
if (process.arch === "arm64") {
|
|
34
|
+
return "officecli-win-arm64.exe";
|
|
35
|
+
}
|
|
36
|
+
if (process.arch === "x64") {
|
|
37
|
+
return "officecli-win-x64.exe";
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(`Unsupported OfficeCLI platform: ${process.platform}/${process.arch}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function readManifest() {
|
|
48
|
+
const manifestRaw = await readFile(manifestPath, "utf8");
|
|
49
|
+
const manifest = JSON.parse(manifestRaw);
|
|
50
|
+
|
|
51
|
+
if (!manifest || typeof manifest.version !== "string" || manifest.version.trim().length === 0) {
|
|
52
|
+
throw new Error("OfficeCLI manifest is missing a valid version.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
version: manifest.version.trim(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function main() {
|
|
61
|
+
if (process.env[SKIP_ENV_NAME] === "1") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (existsSync(join(packageRootPath, "project.json"))) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { version } = await readManifest();
|
|
70
|
+
const assetName = resolveOfficeCliAssetName();
|
|
71
|
+
const targetPath = join(bundledRootPath, assetName);
|
|
72
|
+
|
|
73
|
+
if (existsSync(targetPath)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await mkdir(bundledRootPath, {
|
|
78
|
+
recursive: true,
|
|
79
|
+
mode: 0o755,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const tempPath = `${targetPath}.download${process.platform === "win32" ? ".exe" : ""}`;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(`https://github.com/iOfficeAI/OfficeCLI/releases/download/v${version}/${assetName}`);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`HTTP ${response.status}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const binary = Buffer.from(await response.arrayBuffer());
|
|
91
|
+
await writeFile(tempPath, binary, {
|
|
92
|
+
mode: process.platform === "win32" ? 0o644 : 0o755,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (process.platform !== "win32") {
|
|
96
|
+
await chmod(tempPath, 0o755);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await rename(tempPath, targetPath);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await rm(tempPath, {
|
|
102
|
+
force: true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const reason = error instanceof Error ? error.message : "Unknown error";
|
|
106
|
+
process.stderr.write(`[docyrus] OfficeCLI postinstall download failed: ${reason}\n`);
|
|
107
|
+
process.exitCode = 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await main();
|
|
@@ -28,6 +28,7 @@ const PLAN_ANCHOR_TYPE = "plan-anchor";
|
|
|
28
28
|
const PLAN_CONFIG_FILE = ".pi/plan-policy.json";
|
|
29
29
|
const PLAN_WIDGET_KEY = "plan-mode";
|
|
30
30
|
const PLAN_BRANCH_LABEL = "plan";
|
|
31
|
+
const ARCHITECT_PLAN_ARTIFACT_FILE_NAME = "PLAN.md";
|
|
31
32
|
const ALLOWED_TODO_ACTIONS = new Set(["list", "list-all", "get"]);
|
|
32
33
|
const ALL_THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh"] satisfies ThinkingLevel[]);
|
|
33
34
|
|
|
@@ -96,6 +97,12 @@ export interface IReadPlanPolicyResult {
|
|
|
96
97
|
error?: string;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
interface IPlanningCliEnvironment {
|
|
101
|
+
executable: string;
|
|
102
|
+
entryPath: string;
|
|
103
|
+
scope: "local" | "global";
|
|
104
|
+
}
|
|
105
|
+
|
|
99
106
|
export type IParseResult<T> = { ok: true; value: T } | { ok: false; error: string };
|
|
100
107
|
|
|
101
108
|
let currentPlanState: IPlanSessionState | undefined;
|
|
@@ -120,6 +127,41 @@ function expandUserPath(inputPath: string): string {
|
|
|
120
127
|
return inputPath;
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
function readPlanningCliEnvironment(env: NodeJS.ProcessEnv = process.env): IPlanningCliEnvironment {
|
|
131
|
+
const executable = env.DOCYRUS_CLI_EXECUTABLE?.trim();
|
|
132
|
+
const entryPath = env.DOCYRUS_CLI_ENTRY?.trim();
|
|
133
|
+
const scope = env.DOCYRUS_CLI_SCOPE?.trim() as "local" | "global" | undefined;
|
|
134
|
+
if (!executable || !entryPath || (scope !== "local" && scope !== "global")) {
|
|
135
|
+
throw new Error("Missing Docyrus CLI runtime env. Expected DOCYRUS_CLI_EXECUTABLE, DOCYRUS_CLI_ENTRY, and DOCYRUS_CLI_SCOPE.");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
executable,
|
|
140
|
+
entryPath,
|
|
141
|
+
scope,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function runProjectPlanCliJson<TValue>(
|
|
146
|
+
pi: ExtensionAPI,
|
|
147
|
+
ctx: ExtensionContext,
|
|
148
|
+
args: string[],
|
|
149
|
+
): Promise<TValue> {
|
|
150
|
+
const environment = readPlanningCliEnvironment();
|
|
151
|
+
const scopedArgs = environment.scope === "global" ? ["-g", ...args] : args;
|
|
152
|
+
const result = await pi.exec(environment.executable, [environment.entryPath, ...scopedArgs, "--json"], {
|
|
153
|
+
cwd: ctx.cwd,
|
|
154
|
+
});
|
|
155
|
+
const stdout = result.stdout?.toString().trim() || "";
|
|
156
|
+
const stderr = result.stderr?.toString().trim() || "";
|
|
157
|
+
const output = stdout || stderr;
|
|
158
|
+
if (result.code !== 0 || !output) {
|
|
159
|
+
throw new Error(output || `Command exited with code ${result.code ?? "unknown"}.`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return JSON.parse(output) as TValue;
|
|
163
|
+
}
|
|
164
|
+
|
|
123
165
|
function resolveAgentRootPath(): string {
|
|
124
166
|
const agentDir = process.env.PI_CODING_AGENT_DIR?.trim();
|
|
125
167
|
return agentDir && agentDir.length > 0 ? expandUserPath(agentDir) : path.join(os.homedir(), ".pi", "agent");
|
|
@@ -635,6 +677,28 @@ async function writePlanArtifactFromEvent(event: AgentEndEvent, ctx: ExtensionCo
|
|
|
635
677
|
}
|
|
636
678
|
}
|
|
637
679
|
|
|
680
|
+
async function writeArchitectArtifactFromEvent(event: AgentEndEvent, ctx: ExtensionContext): Promise<void> {
|
|
681
|
+
const state = getPlanState(ctx);
|
|
682
|
+
if (!state?.active || !state.artifactPath || state.mode !== "architect") {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const text = extractLastAssistantText(event.messages ?? []);
|
|
687
|
+
if (!text || parseAskUserRequestFromText(text)) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
try {
|
|
692
|
+
await fs.mkdir(state.artifactPath, { recursive: true });
|
|
693
|
+
await fs.writeFile(path.join(state.artifactPath, ARCHITECT_PLAN_ARTIFACT_FILE_NAME), `${text.trim()}\n`, "utf8");
|
|
694
|
+
} catch (error) {
|
|
695
|
+
if (ctx.hasUI) {
|
|
696
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
697
|
+
ctx.ui.notify(`Failed to write architect plan artifact: ${message}`, "error");
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
638
702
|
export function shouldBlockTodoAction(action: string | undefined): boolean {
|
|
639
703
|
return !!action && !ALLOWED_TODO_ACTIONS.has(action);
|
|
640
704
|
}
|
|
@@ -973,6 +1037,36 @@ export async function endPlanningWorkflow(pi: ExtensionAPI, ctx: ExtensionComman
|
|
|
973
1037
|
setPlanWidget(ctx, undefined);
|
|
974
1038
|
await restoreSourceModel(pi, ctx, state);
|
|
975
1039
|
|
|
1040
|
+
if (state.artifactPath) {
|
|
1041
|
+
try {
|
|
1042
|
+
const syncResult = state.mode === "architect"
|
|
1043
|
+
? await runProjectPlanCliJson<{ updatedTaskIds?: string[] }>(pi, ctx, [
|
|
1044
|
+
"project-plan",
|
|
1045
|
+
"upsert-from-architect",
|
|
1046
|
+
"--artifactDir",
|
|
1047
|
+
state.artifactPath,
|
|
1048
|
+
...(state.task ? ["--brief", state.task] : []),
|
|
1049
|
+
])
|
|
1050
|
+
: await runProjectPlanCliJson<{ updatedTaskIds?: string[] }>(pi, ctx, [
|
|
1051
|
+
"project-plan",
|
|
1052
|
+
"upsert-from-plan",
|
|
1053
|
+
"--artifactPath",
|
|
1054
|
+
state.artifactPath,
|
|
1055
|
+
...(state.task ? ["--task", state.task] : []),
|
|
1056
|
+
]);
|
|
1057
|
+
|
|
1058
|
+
if (ctx.hasUI) {
|
|
1059
|
+
const updatedCount = Array.isArray(syncResult.updatedTaskIds) ? syncResult.updatedTaskIds.length : 0;
|
|
1060
|
+
ctx.ui.notify(`Project plan synced from ${state.mode} artifact (${updatedCount} task update${updatedCount === 1 ? "" : "s"}).`, "info");
|
|
1061
|
+
}
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
if (ctx.hasUI) {
|
|
1064
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1065
|
+
ctx.ui.notify(`Project plan sync failed after ${state.mode}: ${message}`, "warning");
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
976
1070
|
if (ctx.hasUI) {
|
|
977
1071
|
const artifactSuffix = state.artifactPath ? ` Artifact: ${state.artifactPath}` : "";
|
|
978
1072
|
ctx.ui.notify(`Planning session ended.${artifactSuffix}`, "info");
|
|
@@ -1176,6 +1270,7 @@ export default function planExtension(pi: ExtensionAPI) {
|
|
|
1176
1270
|
}
|
|
1177
1271
|
|
|
1178
1272
|
await writePlanArtifactFromEvent(event, ctx);
|
|
1273
|
+
await writeArchitectArtifactFromEvent(event, ctx);
|
|
1179
1274
|
});
|
|
1180
1275
|
|
|
1181
1276
|
pi.on("session_start", async(_event, ctx) => {
|