@docyrus/docyrus 0.0.62 → 0.0.64

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/docyrus",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "private": false,
5
5
  "description": "Docyrus API CLI",
6
6
  "main": "./main.js",
@@ -0,0 +1,183 @@
1
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import type {
5
+ ExtensionAPI,
6
+ ExtensionCommandContext,
7
+ } from "@mariozechner/pi-coding-agent";
8
+ import { DEFAULT_SOUL_ID, SOULS, findSoul } from "../souls/catalog";
9
+
10
+ type Scope = "local" | "global";
11
+
12
+ interface IStoredConfig {
13
+ version?: number;
14
+ activeEnvironmentId?: string;
15
+ environments?: unknown;
16
+ defaultClientId?: string;
17
+ activeSoulId?: string;
18
+ [extraKey: string]: unknown;
19
+ }
20
+
21
+ function resolveScope(): Scope {
22
+ const raw = process.env.DOCYRUS_CLI_SCOPE?.trim();
23
+ if (raw === "global") {
24
+ return "global";
25
+ }
26
+
27
+ return "local";
28
+ }
29
+
30
+ function resolveConfigFilePath(cwd: string): string {
31
+ const scope = resolveScope();
32
+ const root = scope === "global" ? join(homedir(), ".docyrus") : join(cwd, ".docyrus");
33
+ return join(root, "config.json");
34
+ }
35
+
36
+ async function readStoredConfig(cwd: string): Promise<IStoredConfig> {
37
+ const configPath = resolveConfigFilePath(cwd);
38
+ try {
39
+ const raw = await readFile(configPath, "utf8");
40
+ const parsed = JSON.parse(raw) as IStoredConfig;
41
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
42
+ return parsed;
43
+ }
44
+ }
45
+ catch (error) {
46
+ const code = (error as NodeJS.ErrnoException | null)?.code;
47
+ if (code !== "ENOENT") {
48
+ throw error;
49
+ }
50
+ }
51
+
52
+ return {};
53
+ }
54
+
55
+ async function writeStoredConfig(cwd: string, config: IStoredConfig): Promise<void> {
56
+ const configPath = resolveConfigFilePath(cwd);
57
+ await mkdir(dirname(configPath), {
58
+ recursive: true,
59
+ mode: 0o700,
60
+ });
61
+
62
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, {
63
+ encoding: "utf8",
64
+ mode: 0o600,
65
+ });
66
+
67
+ await chmod(configPath, 0o600);
68
+ }
69
+
70
+ async function getPersistedSoulId(cwd: string): Promise<string> {
71
+ const config = await readStoredConfig(cwd);
72
+ const stored = typeof config.activeSoulId === "string" ? config.activeSoulId.trim() : "";
73
+ if (stored && findSoul(stored)) {
74
+ return stored;
75
+ }
76
+
77
+ return DEFAULT_SOUL_ID;
78
+ }
79
+
80
+ async function persistSoulId(cwd: string, soulId: string): Promise<void> {
81
+ const config = await readStoredConfig(cwd);
82
+ await writeStoredConfig(cwd, {
83
+ ...config,
84
+ activeSoulId: soulId,
85
+ });
86
+ }
87
+
88
+ function parseSoulArg(rawArgs: string): string | null {
89
+ const trimmed = rawArgs.trim();
90
+ return trimmed.length > 0 ? trimmed.split(/\s+/u)[0] || null : null;
91
+ }
92
+
93
+ function renderSoulList(activeSoulId: string, runtimeSoulId: string): string {
94
+ const lines: string[] = [
95
+ "# Available Souls",
96
+ "",
97
+ "Use `/soul <id>` to pick one. Changes take effect on the **next** agent session (restart the agent).",
98
+ "",
99
+ ];
100
+
101
+ for (const soul of SOULS) {
102
+ const markers: string[] = [];
103
+ if (soul.id === runtimeSoulId) {
104
+ markers.push("running");
105
+ }
106
+ if (soul.id === activeSoulId && soul.id !== runtimeSoulId) {
107
+ markers.push("pending");
108
+ }
109
+ if (soul.id === activeSoulId && soul.id === runtimeSoulId) {
110
+ markers.length = 0;
111
+ markers.push("active");
112
+ }
113
+
114
+ const suffix = markers.length > 0 ? ` _(${markers.join(", ")})_` : "";
115
+ lines.push(`- \`${soul.id}\` — **${soul.name}**: ${soul.description}${suffix}`);
116
+ }
117
+
118
+ lines.push("");
119
+ lines.push(`Current: \`${runtimeSoulId}\` (running) · \`${activeSoulId}\` (persisted for next session)`);
120
+ return lines.join("\n");
121
+ }
122
+
123
+ function renderUsage(activeSoulId: string, runtimeSoulId: string): string {
124
+ return [
125
+ "# /soul",
126
+ "",
127
+ "Switch the agent's communication style (SOUL).",
128
+ "",
129
+ "- `/soul` — show this help and current selection.",
130
+ "- `/soul <id>` — persist a new soul; takes effect on the next session.",
131
+ "- `/souls` — list every available soul with descriptions.",
132
+ "",
133
+ `Currently running: \`${runtimeSoulId}\``,
134
+ `Persisted for next session: \`${activeSoulId}\``,
135
+ ].join("\n");
136
+ }
137
+
138
+ export default function(pi: ExtensionAPI) {
139
+ pi.registerCommand("souls", {
140
+ description: "List available agent souls (communication styles)",
141
+ handler: async(_rawArgs: string, ctx: ExtensionCommandContext) => {
142
+ const activeSoulId = await getPersistedSoulId(ctx.cwd);
143
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
144
+ if (ctx.hasUI) {
145
+ ctx.ui.notify(renderSoulList(activeSoulId, runtimeSoulId), "info");
146
+ }
147
+ },
148
+ });
149
+
150
+ pi.registerCommand("soul", {
151
+ description: "Show or change the agent's soul (communication style)",
152
+ handler: async(rawArgs: string, ctx: ExtensionCommandContext) => {
153
+ const requested = parseSoulArg(rawArgs);
154
+ const activeSoulId = await getPersistedSoulId(ctx.cwd);
155
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
156
+
157
+ if (!requested) {
158
+ if (ctx.hasUI) {
159
+ ctx.ui.notify(renderUsage(activeSoulId, runtimeSoulId), "info");
160
+ }
161
+ return;
162
+ }
163
+
164
+ const candidate = findSoul(requested);
165
+ if (!candidate) {
166
+ const known = SOULS.map((soul) => soul.id).join(", ");
167
+ if (ctx.hasUI) {
168
+ ctx.ui.notify(`Unknown soul '${requested}'. Known souls: ${known}.`, "warning");
169
+ }
170
+ return;
171
+ }
172
+
173
+ await persistSoulId(ctx.cwd, candidate.id);
174
+
175
+ if (ctx.hasUI) {
176
+ const suffix = candidate.id === runtimeSoulId
177
+ ? " Already running — no restart needed."
178
+ : " Takes effect on the next agent session — restart with `/exit` and relaunch.";
179
+ ctx.ui.notify(`Soul set to \`${candidate.id}\` — ${candidate.name}.${suffix}`, "info");
180
+ }
181
+ },
182
+ });
183
+ }
@@ -1,14 +1,17 @@
1
1
  ---
2
2
  name: grill-me
3
3
  description: >
4
- Interview the user relentlessly about a plan, architecture, or data model design until reaching shared understanding, resolving each branch of the decision tree. Use when the user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
5
-
6
- **Triggers — use this skill when:**
7
- - User says "grill me", "stress test this", "challenge my design"
8
- - User asks to "poke holes", "find gaps", "review my thinking"
9
- - User wants to validate a plan before implementing
10
- - User says "interview me about this", "question my decisions"
11
- - Used after `/architect` or `/plan` when the user wants to pressure-test the output
4
+ Stress-test a plan, architecture, or data model design by interviewing the user one question at a time, walking each branch of the decision tree until every critical choice is resolved. Tailored to Docyrus projects — probes data source design, field types, enum choices, relations and dependencies, access control, edge cases, and implementation feasibility against the current platform. Tracks resolved decisions, surfaces open questions, and offers to update `PLAN.md` / `data-sources.plan.json` with the agreed changes when the interview finishes.
5
+
6
+ **Triggers — use this skill when:**
7
+ - User says "grill me", "stress test this", "challenge my design", "pressure-test this"
8
+ - User asks to "poke holes", "find gaps", "review my thinking"
9
+ - User wants to validate a plan before implementing
10
+ - User says "interview me about this", "question my decisions", "ask me hard questions"
11
+ - Used after `/architect` or `/plan` when the user wants to pressure-test the output
12
+ - User is about to commit to a schema, data model, or architectural decision and wants a second pass
13
+
14
+ **Covers:** Plans and architectures for Docyrus-backed apps — data sources, fields, enums, relations, apps, queries, and the surrounding access-control and implementation-feasibility questions. Works best when there is a plan artifact on disk (PLAN.md, data-sources.plan.json, discovery snapshot) to anchor the conversation.
12
15
  ---
13
16
 
14
17
  # Grill Me