@tplog/pi-zendy 0.4.0 → 0.4.1

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 CHANGED
@@ -14,6 +14,7 @@ zendy is a single pi extension that provides:
14
14
  - **Slash Commands** — `/zendy-config` to set up credentials, `/zendy-status` to check connectivity.
15
15
  - **Skill** — a `zendy` skill (also `/skill:zendy`) that teaches the agent the ticket-analysis workflow, so "analyze ticket #1959" reliably uses the right tools in the right order.
16
16
  - **Session Safety** — Automatic workspace isolation and cleanup for source code analysis.
17
+ - **Source Registry** — Bundled Dify source repository defaults. Enterprise repos use SSH URLs, so GitHub SSH permissions remain the access gate.
17
18
 
18
19
  Typical workflow:
19
20
 
@@ -73,7 +74,7 @@ The agent can call these tools directly:
73
74
  | `zendy_whoami` | Check the currently authenticated Zendesk identity |
74
75
  | `zendy_helm_get` | Query Helm chart values, images, validation by version |
75
76
  | `zendy_kg_search` | Semantic search over historical tickets |
76
- | `zendy_source_status` | Check source analysis workspace |
77
+ | `zendy_source_status` | Check source analysis workspace and bundled Dify source repository registry |
77
78
 
78
79
  ## How it works
79
80
 
@@ -81,3 +82,5 @@ zendy registers as a pi extension package. The extension provides tools (callabl
81
82
  slash commands (for human engineers), and session lifecycle hooks (workspace creation, cleanup).
82
83
  All data access goes through direct REST APIs — no `zcli`, `zendesk-kg`, or other CLI tools
83
84
  are required at runtime.
85
+
86
+ For source analysis, zendy ships default repository entries for Dify Enterprise backend/frontend and related public Dify repositories. The Enterprise entries are SSH URLs; users without GitHub SSH access can install zendy, but source clone attempts will fail at Git authentication.
@@ -1,6 +1,7 @@
1
1
  export interface ZendyConfig {
2
2
  zendesk?: ZendeskConfig;
3
3
  zendeskKg?: ZendeskKgConfig;
4
+ sourceRepos?: Record<string, SourceRepoConfig>;
4
5
  }
5
6
  export interface ZendeskConfig {
6
7
  subdomain?: string;
@@ -11,4 +12,11 @@ export interface ZendeskKgConfig {
11
12
  apiUrl?: string;
12
13
  apiKey?: string;
13
14
  }
15
+ export interface SourceRepoConfig {
16
+ url: string;
17
+ description?: string;
18
+ visibility?: "private" | "public";
19
+ area?: "backend" | "frontend" | "oss" | "plugin" | "sandbox";
20
+ }
14
21
  export declare const DEFAULT_KG_API_URL = "https://zendesk-ticket-retriever.vercel.app";
22
+ export declare const DEFAULT_SOURCE_REPOS: Record<string, SourceRepoConfig>;
@@ -1,2 +1,34 @@
1
1
  // Config schema and types for zendy configuration (~/.zendy/config.json).
2
2
  export const DEFAULT_KG_API_URL = "https://zendesk-ticket-retriever.vercel.app";
3
+ export const DEFAULT_SOURCE_REPOS = {
4
+ enterpriseBackend: {
5
+ url: "git@github.com:langgenius/dify-enterprise.git",
6
+ description: "Dify Enterprise backend code",
7
+ visibility: "private",
8
+ area: "backend",
9
+ },
10
+ enterpriseFrontend: {
11
+ url: "git@github.com:langgenius/dify-enterprise-frontend.git",
12
+ description: "Dify Enterprise frontend code",
13
+ visibility: "private",
14
+ area: "frontend",
15
+ },
16
+ difyOss: {
17
+ url: "https://github.com/langgenius/dify.git",
18
+ description: "Open-source Dify repository",
19
+ visibility: "public",
20
+ area: "oss",
21
+ },
22
+ pluginDaemon: {
23
+ url: "https://github.com/langgenius/dify-plugin-daemon.git",
24
+ description: "Dify plugin daemon",
25
+ visibility: "public",
26
+ area: "plugin",
27
+ },
28
+ sandbox: {
29
+ url: "https://github.com/langgenius/dify-sandbox.git",
30
+ description: "Dify sandbox",
31
+ visibility: "public",
32
+ area: "sandbox",
33
+ },
34
+ };
@@ -1,4 +1,4 @@
1
- import type { ZendyConfig } from "./schema.js";
1
+ import { type ZendyConfig } from "./schema.js";
2
2
  export declare function getConfig(): ZendyConfig;
3
3
  export declare function writeConfig(config: ZendyConfig): void;
4
4
  export declare function configPath(): string;
@@ -3,6 +3,7 @@
3
3
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { homedir } from "node:os";
6
+ import { DEFAULT_SOURCE_REPOS } from "./schema.js";
6
7
  const CONFIG_DIR = join(homedir(), ".zendy");
7
8
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
8
9
  // ── Env overrides ─────────────────────────────────────────────────────
@@ -52,6 +53,10 @@ export function getConfig() {
52
53
  ...fileConfig.zendeskKg,
53
54
  ...envK,
54
55
  },
56
+ sourceRepos: {
57
+ ...DEFAULT_SOURCE_REPOS,
58
+ ...fileConfig.sourceRepos,
59
+ },
55
60
  };
56
61
  }
57
62
  export function writeConfig(config) {
@@ -66,6 +66,9 @@ async function configCommand(_args: string, ctx: {
66
66
  lines.push(
67
67
  `KG: ${c.zendeskKg?.apiKey ? `apiKey=*** (set), apiUrl=${c.zendeskKg.apiUrl || "default"}` : "not configured"}`,
68
68
  );
69
+ const repoEntries = Object.entries(c.sourceRepos ?? {});
70
+ const privateCount = repoEntries.filter(([, repo]) => repo.visibility === "private").length;
71
+ lines.push(`Source repos: ${repoEntries.length} configured (${privateCount} private SSH-gated)`);
69
72
  ctx.ui.notify(`[zendy-config]\n${lines.join("\n")}`, "info");
70
73
  }
71
74
  }
@@ -105,6 +108,10 @@ async function statusCommand(_args: string, ctx: {
105
108
  srcDir ? ` ✓ Source workspace — ${srcDir}` : " - Source workspace — not active (will be created on session start)",
106
109
  );
107
110
 
111
+ const repoEntries = Object.entries(getConfig().sourceRepos ?? {});
112
+ const privateCount = repoEntries.filter(([, repo]) => repo.visibility === "private").length;
113
+ lines.push(` ✓ Source repos — ${repoEntries.length} configured (${privateCount} private SSH-gated)`);
114
+
108
115
  ctx.ui.notify(`Zendy Status:\n${lines.join("\n")}`, hasIssue ? "error" : "info");
109
116
  }
110
117
 
@@ -6,6 +6,7 @@ import { Type } from "typebox";
6
6
  import * as zendesk from "../dist/clients/zendesk.js";
7
7
  import * as helm from "../dist/clients/helm-watchdog.js";
8
8
  import * as kg from "../dist/clients/zendesk-kg.js";
9
+ import { getConfig } from "../dist/config/store.js";
9
10
 
10
11
  // IMPORTANT: only `content` is sent to the model; `details` is UI/extension
11
12
  // metadata the model never sees (verified empirically against pi 0.79). Any
@@ -248,15 +249,26 @@ function registerSourceTools(pi: ExtensionAPI): void {
248
249
  pi.registerTool({
249
250
  name: "zendy_source_status",
250
251
  label: "Zendy Source Status",
251
- description: "Report the zendy source-analysis workspace path and whether source cloning is authorized.",
252
- promptSnippet: "Check source workspace status with zendy_source_status.",
252
+ description: "Report the zendy source-analysis workspace path, bundled source repositories, and source-cloning authorization rules.",
253
+ promptSnippet: "Check source workspace and repo registry with zendy_source_status.",
253
254
  parameters: Type.Object({}),
254
255
  async execute() {
255
256
  const workspace = process.env["ZENDY_SRC_DIR"] ?? null;
256
- const note = "Source cloning/search requires explicit user permission per zendy workflow rules. Ask before cloning private source.";
257
+ const repos = getConfig().sourceRepos ?? {};
258
+ const repoLines = Object.entries(repos).map(([name, repo]) => {
259
+ const visibility = repo.visibility ?? "public";
260
+ const access = visibility === "private" ? "SSH access required" : "public";
261
+ return `- ${name}: ${repo.url} (${access})${repo.description ? ` — ${repo.description}` : ""}`;
262
+ });
263
+ const note = "Source cloning/search requires explicit user permission per zendy workflow rules. Private Enterprise repos are SSH-gated; clone failures usually mean the user lacks GitHub access.";
257
264
  return textResult(
258
- `Source workspace: ${workspace ?? "(not active — created on session start)"}\n${note}`,
259
- { workspace, note },
265
+ [
266
+ `Source workspace: ${workspace ?? "(not active — created on session start)"}`,
267
+ "Configured source repositories:",
268
+ repoLines.length ? repoLines.join("\n") : "- (none configured)",
269
+ note,
270
+ ].join("\n"),
271
+ { workspace, repos, note },
260
272
  );
261
273
  },
262
274
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tplog/pi-zendy",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Pi package for Dify Enterprise support ticket analysis",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@ support engineer can analyze a ticket end-to-end without leaving the terminal.
25
25
  | `zendy_whoami` | Verify which Zendesk account is authenticated |
26
26
  | `zendy_helm_get` | Dify Helm chart data by exact version: values.yaml, images, validation, latest |
27
27
  | `zendy_kg_search` | Semantic search over historical tickets; returns ticketIds |
28
- | `zendy_source_status` | Check the source-analysis workspace path and authorization rules |
28
+ | `zendy_source_status` | Check the source-analysis workspace, bundled repo registry, and authorization rules |
29
29
 
30
30
  Use these tools for all data access. Do not use zcli, curl, or any external Zendesk
31
31
  CLI. If credentials are missing, tell the user to run `/zendy-config`; check
@@ -55,6 +55,8 @@ connectivity with `/zendy-status`.
55
55
  - **Source code analysis requires explicit user permission.** If config-level
56
56
  analysis cannot settle the question, ask the user whether to check the source
57
57
  (e.g. "需要我去源码确认吗?") — never clone without permission. Use
58
- `zendy_source_status` to find the workspace.
58
+ `zendy_source_status` to find the workspace and bundled source repo URLs.
59
+ Enterprise backend/frontend repos are SSH-gated; if cloning fails, ask the user
60
+ to confirm their GitHub SSH access rather than trying alternate credentials.
59
61
  - **Inspect before filtering.** On the first call to an unfamiliar response shape,
60
62
  look at the structure before writing filter expressions; never assume field names.