@tplog/pi-zendy 0.3.6 → 0.3.8
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 +2 -0
- package/dist/clients/zendesk-kg.js +4 -19
- package/dist/clients/zendesk.js +4 -18
- package/dist/config/resolve.d.ts +3 -0
- package/dist/config/resolve.js +40 -0
- package/dist/config/store.d.ts +1 -3
- package/dist/config/store.js +0 -12
- package/dist/index.js +11 -34
- package/dist/preflight.js +2 -39
- package/dist/source-cleanup.js +3 -3
- package/extensions/commands.ts +2 -1
- package/package.json +6 -2
- package/skills/zendy/SKILL.md +60 -0
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ zendy is a single pi extension that provides:
|
|
|
12
12
|
|
|
13
13
|
- **LLM Tools** — Direct API access to Zendesk, Helm Watchdog, and Knowledge Graph. No external CLI dependencies.
|
|
14
14
|
- **Slash Commands** — `/zendy-config` to set up credentials, `/zendy-status` to check connectivity.
|
|
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.
|
|
15
16
|
- **Session Safety** — Automatic workspace isolation and cleanup for source code analysis.
|
|
16
17
|
|
|
17
18
|
Typical workflow:
|
|
@@ -69,6 +70,7 @@ The agent can call these tools directly:
|
|
|
69
70
|
|------|-------------|
|
|
70
71
|
| `zendy_ticket_get` | Fetch ticket metadata, comments, and user info |
|
|
71
72
|
| `zendy_ticket_search` | Search live Zendesk tickets |
|
|
73
|
+
| `zendy_whoami` | Check the currently authenticated Zendesk identity |
|
|
72
74
|
| `zendy_helm_get` | Query Helm chart values, images, validation by version |
|
|
73
75
|
| `zendy_kg_search` | Semantic search over historical tickets |
|
|
74
76
|
| `zendy_source_status` | Check source analysis workspace |
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
// Direct Zendesk Knowledge Graph API client. No zendesk-kg CLI dependency.
|
|
2
|
-
import {
|
|
3
|
-
import { migrateLegacyConfig, getLegacyKgEnv } from "../config/migrate.js";
|
|
2
|
+
import { resolveKgConfig } from "../config/resolve.js";
|
|
4
3
|
import { DEFAULT_KG_API_URL } from "../config/schema.js";
|
|
5
4
|
// ── Resolve config ─────────────────────────────────────────────────────
|
|
6
5
|
function resolveConfig() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return zendeskKg;
|
|
11
|
-
// Try legacy migration
|
|
12
|
-
migrateLegacyConfig();
|
|
13
|
-
const cfg2 = getConfig();
|
|
14
|
-
if (cfg2.zendeskKg?.apiKey)
|
|
15
|
-
return cfg2.zendeskKg;
|
|
16
|
-
// Try legacy .env directly
|
|
17
|
-
const legacy = getLegacyKgEnv();
|
|
18
|
-
if (legacy?.["RETRIEVER_API_KEY"]) {
|
|
19
|
-
return {
|
|
20
|
-
apiUrl: legacy["RETRIEVER_API_URL"] || undefined,
|
|
21
|
-
apiKey: legacy["RETRIEVER_API_KEY"],
|
|
22
|
-
};
|
|
23
|
-
}
|
|
6
|
+
const cfg = resolveKgConfig();
|
|
7
|
+
if (cfg)
|
|
8
|
+
return cfg;
|
|
24
9
|
throw new Error("Zendesk KG not configured. Use /zendy-config to set up credentials.\n" +
|
|
25
10
|
"Or set env: ZENDY_KG_API_KEY");
|
|
26
11
|
}
|
package/dist/clients/zendesk.js
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
1
|
// Direct Zendesk REST API client. No zcli dependency.
|
|
2
|
-
import {
|
|
3
|
-
import { migrateLegacyConfig, getLegacyZendeskConfig } from "../config/migrate.js";
|
|
2
|
+
import { resolveZendeskConfig } from "../config/resolve.js";
|
|
4
3
|
// ── Resolve config ─────────────────────────────────────────────────────
|
|
5
4
|
function resolveConfig() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return zendesk;
|
|
10
|
-
}
|
|
11
|
-
// Try legacy migration
|
|
12
|
-
migrateLegacyConfig();
|
|
13
|
-
const cfg2 = getConfig();
|
|
14
|
-
if (cfg2.zendesk?.subdomain && cfg2.zendesk?.email && cfg2.zendesk?.apiToken) {
|
|
15
|
-
return cfg2.zendesk;
|
|
16
|
-
}
|
|
17
|
-
// Try legacy zcli config directly
|
|
18
|
-
const legacy = getLegacyZendeskConfig();
|
|
19
|
-
if (legacy?.subdomain && legacy?.email && legacy?.api_token) {
|
|
20
|
-
return { subdomain: legacy.subdomain, email: legacy.email, apiToken: legacy.api_token };
|
|
21
|
-
}
|
|
5
|
+
const cfg = resolveZendeskConfig();
|
|
6
|
+
if (cfg)
|
|
7
|
+
return cfg;
|
|
22
8
|
throw new Error("Zendesk not configured. Use /zendy-config to set up credentials.\n" +
|
|
23
9
|
"Or set env: ZENDY_ZENDESK_SUBDOMAIN, ZENDY_ZENDESK_EMAIL, ZENDY_ZENDESK_API_TOKEN");
|
|
24
10
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Single source of truth for resolving effective credentials.
|
|
2
|
+
//
|
|
3
|
+
// Precedence (same for both Zendesk and KG):
|
|
4
|
+
// 1. zendy config — env vars override ~/.zendy/config.json
|
|
5
|
+
// 2. legacy migration — import legacy zcli / zendesk-kg files into zendy config
|
|
6
|
+
// 3. direct legacy files — read the legacy config without migrating
|
|
7
|
+
//
|
|
8
|
+
// Returns undefined when nothing resolves; callers decide whether that is fatal.
|
|
9
|
+
// Lives above store.ts and migrate.ts to compose them without a dependency cycle
|
|
10
|
+
// (migrate.ts already depends on store.ts).
|
|
11
|
+
import { getConfig } from "./store.js";
|
|
12
|
+
import { migrateLegacyConfig, getLegacyZendeskConfig, getLegacyKgEnv } from "./migrate.js";
|
|
13
|
+
export function resolveZendeskConfig() {
|
|
14
|
+
const cfg = getConfig().zendesk;
|
|
15
|
+
if (cfg?.subdomain && cfg?.email && cfg?.apiToken)
|
|
16
|
+
return cfg;
|
|
17
|
+
migrateLegacyConfig();
|
|
18
|
+
const migrated = getConfig().zendesk;
|
|
19
|
+
if (migrated?.subdomain && migrated?.email && migrated?.apiToken)
|
|
20
|
+
return migrated;
|
|
21
|
+
const legacy = getLegacyZendeskConfig();
|
|
22
|
+
if (legacy?.subdomain && legacy?.email && legacy?.api_token) {
|
|
23
|
+
return { subdomain: legacy.subdomain, email: legacy.email, apiToken: legacy.api_token };
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function resolveKgConfig() {
|
|
28
|
+
const cfg = getConfig().zendeskKg;
|
|
29
|
+
if (cfg?.apiKey)
|
|
30
|
+
return cfg;
|
|
31
|
+
migrateLegacyConfig();
|
|
32
|
+
const migrated = getConfig().zendeskKg;
|
|
33
|
+
if (migrated?.apiKey)
|
|
34
|
+
return migrated;
|
|
35
|
+
const legacy = getLegacyKgEnv();
|
|
36
|
+
if (legacy?.["RETRIEVER_API_KEY"]) {
|
|
37
|
+
return { apiUrl: legacy["RETRIEVER_API_URL"] || undefined, apiKey: legacy["RETRIEVER_API_KEY"] };
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
package/dist/config/store.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type { ZendyConfig
|
|
1
|
+
import type { ZendyConfig } from "./schema.js";
|
|
2
2
|
export declare function getConfig(): ZendyConfig;
|
|
3
|
-
export declare function getZendeskConfig(): ZendeskConfig | undefined;
|
|
4
|
-
export declare function getZendeskKgConfig(): ZendeskKgConfig | undefined;
|
|
5
3
|
export declare function writeConfig(config: ZendyConfig): void;
|
|
6
4
|
export declare function configPath(): string;
|
|
7
5
|
export declare function configExists(): boolean;
|
package/dist/config/store.js
CHANGED
|
@@ -54,18 +54,6 @@ export function getConfig() {
|
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
export function getZendeskConfig() {
|
|
58
|
-
const c = getConfig().zendesk;
|
|
59
|
-
if (c?.subdomain && c?.email && c?.apiToken)
|
|
60
|
-
return c;
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
export function getZendeskKgConfig() {
|
|
64
|
-
const c = getConfig().zendeskKg;
|
|
65
|
-
if (c?.apiKey)
|
|
66
|
-
return c;
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
57
|
export function writeConfig(config) {
|
|
70
58
|
mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
71
59
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
3
2
|
import { join, dirname } from "node:path";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
3
|
import { execFileSync, spawn } from "node:child_process";
|
|
6
4
|
import { fileURLToPath } from "node:url";
|
|
7
5
|
import { createRequire } from "node:module";
|
|
8
|
-
import { createHash } from "node:crypto";
|
|
9
6
|
import { runPreflight, printPreflightReport, promptCoreMissing } from "./preflight.js";
|
|
10
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
8
|
const __dirname = dirname(__filename);
|
|
@@ -14,34 +11,15 @@ const pkg = require("../package.json");
|
|
|
14
11
|
const VERSION = pkg.version;
|
|
15
12
|
// Resolve package root (one level up from dist/)
|
|
16
13
|
const PKG_ROOT = join(__dirname, "..");
|
|
17
|
-
//
|
|
14
|
+
// The extension is loaded directly from the package's own extensions/ dir,
|
|
15
|
+
// where zendy.ts and its sibling imports (tools.ts, commands.ts) live together.
|
|
16
|
+
// This is the same file pi loads via the `pi.extensions` manifest field, so the
|
|
17
|
+
// `zendy` launcher and `pi install` behave identically — no separate extraction step.
|
|
18
18
|
const EXT_ZENDY = join(PKG_ROOT, "extensions", "zendy.ts");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const SOURCE_FILES = [
|
|
24
|
-
EXT_ZENDY,
|
|
25
|
-
];
|
|
26
|
-
function contentHash() {
|
|
27
|
-
const hash = createHash("sha256");
|
|
28
|
-
for (const f of SOURCE_FILES) {
|
|
29
|
-
hash.update(readFileSync(f, "utf-8"));
|
|
30
|
-
}
|
|
31
|
-
return hash.digest("hex").slice(0, 16);
|
|
32
|
-
}
|
|
33
|
-
function ensureExtracted(base) {
|
|
34
|
-
const marker = join(base, ".extracted");
|
|
35
|
-
const currentHash = contentHash();
|
|
36
|
-
// Re-extract if marker is missing or hash has changed
|
|
37
|
-
if (existsSync(marker) && readFileSync(marker, "utf-8").trim() === currentHash) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const extDir = join(base, "extensions");
|
|
41
|
-
mkdirSync(extDir, { recursive: true });
|
|
42
|
-
writeFileSync(join(extDir, "zendy.ts"), readFileSync(EXT_ZENDY, "utf-8"));
|
|
43
|
-
writeFileSync(marker, currentHash);
|
|
44
|
-
}
|
|
19
|
+
// Skill describing zendy and the ticket-analysis workflow. pi discovers it via
|
|
20
|
+
// the pi.skills manifest field when installed as a package; the launcher passes
|
|
21
|
+
// it explicitly so both load paths expose the same skill.
|
|
22
|
+
const SKILL_ZENDY = join(PKG_ROOT, "skills", "zendy");
|
|
45
23
|
function findPi() {
|
|
46
24
|
try {
|
|
47
25
|
return execFileSync("which", ["pi"], { encoding: "utf-8" }).trim();
|
|
@@ -105,13 +83,12 @@ async function main() {
|
|
|
105
83
|
}
|
|
106
84
|
// Remove --skip-preflight before passing to pi
|
|
107
85
|
const filteredArgs = userArgs.filter((a) => a !== "--skip-preflight");
|
|
108
|
-
const base = cacheDir();
|
|
109
|
-
ensureExtracted(base);
|
|
110
86
|
const pi = findPi();
|
|
111
|
-
const extensionsDir = join(base, "extensions");
|
|
112
87
|
const args = [
|
|
113
88
|
"--extension",
|
|
114
|
-
|
|
89
|
+
EXT_ZENDY,
|
|
90
|
+
"--skill",
|
|
91
|
+
SKILL_ZENDY,
|
|
115
92
|
];
|
|
116
93
|
// Pass through all user arguments
|
|
117
94
|
args.push(...filteredArgs);
|
package/dist/preflight.js
CHANGED
|
@@ -3,8 +3,8 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { createInterface } from "node:readline";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { resolveZendeskConfig, resolveKgConfig } from "./config/resolve.js";
|
|
7
|
+
import { DEFAULT_KG_API_URL } from "./config/schema.js";
|
|
8
8
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
9
9
|
function exec(cmd, args, timeoutMs = 5000) {
|
|
10
10
|
return new Promise((resolve) => {
|
|
@@ -60,25 +60,6 @@ async function checkPi() {
|
|
|
60
60
|
}
|
|
61
61
|
return { ...base, status: "ok", hint: "" };
|
|
62
62
|
}
|
|
63
|
-
function resolveZendeskConfig() {
|
|
64
|
-
// 1. zendy config (env or ~/.zendy/config.json)
|
|
65
|
-
const cfg = getConfig();
|
|
66
|
-
if (cfg.zendesk?.subdomain && cfg.zendesk?.email && cfg.zendesk?.apiToken) {
|
|
67
|
-
return { subdomain: cfg.zendesk.subdomain, email: cfg.zendesk.email, apiToken: cfg.zendesk.apiToken };
|
|
68
|
-
}
|
|
69
|
-
// 2. Try legacy migration
|
|
70
|
-
migrateLegacyConfig();
|
|
71
|
-
const cfg2 = getConfig();
|
|
72
|
-
if (cfg2.zendesk?.subdomain && cfg2.zendesk?.email && cfg2.zendesk?.apiToken) {
|
|
73
|
-
return { subdomain: cfg2.zendesk.subdomain, email: cfg2.zendesk.email, apiToken: cfg2.zendesk.apiToken };
|
|
74
|
-
}
|
|
75
|
-
// 3. Legacy zcli config
|
|
76
|
-
const legacy = getLegacyZendeskConfig();
|
|
77
|
-
if (legacy?.subdomain && legacy?.email && legacy?.api_token) {
|
|
78
|
-
return { subdomain: legacy.subdomain, email: legacy.email, apiToken: legacy.api_token };
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
63
|
async function checkZendesk() {
|
|
83
64
|
const base = {
|
|
84
65
|
name: "zendesk",
|
|
@@ -121,24 +102,6 @@ async function checkZendesk() {
|
|
|
121
102
|
};
|
|
122
103
|
}
|
|
123
104
|
}
|
|
124
|
-
function resolveKgConfig() {
|
|
125
|
-
// 1. zendy config
|
|
126
|
-
const cfg = getConfig();
|
|
127
|
-
if (cfg.zendeskKg?.apiKey)
|
|
128
|
-
return { apiUrl: cfg.zendeskKg.apiUrl, apiKey: cfg.zendeskKg.apiKey };
|
|
129
|
-
// 2. Try legacy migration
|
|
130
|
-
migrateLegacyConfig();
|
|
131
|
-
const cfg2 = getConfig();
|
|
132
|
-
if (cfg2.zendeskKg?.apiKey)
|
|
133
|
-
return { apiUrl: cfg2.zendeskKg.apiUrl, apiKey: cfg2.zendeskKg.apiKey };
|
|
134
|
-
// 3. Legacy .env
|
|
135
|
-
const legacy = getLegacyKgEnv();
|
|
136
|
-
if (legacy?.["RETRIEVER_API_KEY"]) {
|
|
137
|
-
return { apiUrl: legacy["RETRIEVER_API_URL"] || undefined, apiKey: legacy["RETRIEVER_API_KEY"] };
|
|
138
|
-
}
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
const DEFAULT_KG_API_URL = "https://zendesk-ticket-retriever.vercel.app";
|
|
142
105
|
async function checkZendeskKgApi() {
|
|
143
106
|
const base = {
|
|
144
107
|
name: "zendesk-kg",
|
package/dist/source-cleanup.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Used by:
|
|
8
8
|
// - `src/cleanup-src.ts` — the `zendy cleanup-src` CLI subcommand.
|
|
9
|
-
// - `extensions/
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// - `extensions/zendy.ts` — the pi extension inlines the tiny session-dir
|
|
10
|
+
// helpers it needs so it can be loaded as a self-contained TS file by pi
|
|
11
|
+
// at runtime.
|
|
12
12
|
import { readdir, stat, rm } from "node:fs/promises";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
async function dirSize(path) {
|
package/extensions/commands.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
5
5
|
import { getConfig, writeConfig, configPath, configExists } from "../dist/config/store.js";
|
|
6
6
|
import { migrateLegacyConfig } from "../dist/config/migrate.js";
|
|
7
7
|
import type { ZendyConfig } from "../dist/config/schema.js";
|
|
8
|
+
import { DEFAULT_KG_API_URL } from "../dist/config/schema.js";
|
|
8
9
|
import * as zendesk from "../dist/clients/zendesk.js";
|
|
9
10
|
import * as kg from "../dist/clients/zendesk-kg.js";
|
|
10
11
|
|
|
@@ -44,7 +45,7 @@ async function configCommand(_args: string, ctx: {
|
|
|
44
45
|
} else if (action === "Knowledge Graph credentials") {
|
|
45
46
|
const apiUrl = await ctx.ui.input(
|
|
46
47
|
"Knowledge Graph API URL (leave empty for default)",
|
|
47
|
-
config.zendeskKg?.apiUrl ??
|
|
48
|
+
config.zendeskKg?.apiUrl ?? DEFAULT_KG_API_URL,
|
|
48
49
|
);
|
|
49
50
|
const apiKey = await ctx.ui.input("Knowledge Graph API key", "");
|
|
50
51
|
config.zendeskKg = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tplog/pi-zendy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Pi package for Dify Enterprise support ticket analysis",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,12 +19,16 @@
|
|
|
19
19
|
"pi": {
|
|
20
20
|
"extensions": [
|
|
21
21
|
"./extensions/zendy.ts"
|
|
22
|
+
],
|
|
23
|
+
"skills": [
|
|
24
|
+
"./skills"
|
|
22
25
|
]
|
|
23
26
|
},
|
|
24
27
|
"files": [
|
|
25
28
|
"dist",
|
|
26
29
|
"extensions",
|
|
27
|
-
"prompts"
|
|
30
|
+
"prompts",
|
|
31
|
+
"skills"
|
|
28
32
|
],
|
|
29
33
|
"publishConfig": {
|
|
30
34
|
"access": "public"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zendy
|
|
3
|
+
description: >
|
|
4
|
+
Dify Enterprise support ticket analysis using the zendy tools. Use whenever the
|
|
5
|
+
user mentions a Zendesk ticket number or asks to analyze, investigate, or reply to
|
|
6
|
+
a support ticket (e.g. "analyze ticket #1959", "分析工单", "工单分析", "チケットを調査"),
|
|
7
|
+
asks about a customer's Dify Helm chart values or version defaults, wants to find
|
|
8
|
+
similar historical tickets, or needs a customer reply drafted. Covers the full
|
|
9
|
+
workflow: fetch ticket → identify Dify version → check Helm values → search
|
|
10
|
+
similar tickets → draft reply in the ticket's language.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# zendy — Dify Enterprise Support Ticket Analysis
|
|
14
|
+
|
|
15
|
+
zendy is a support-engineering harness for Dify Enterprise. It registers tools for
|
|
16
|
+
Zendesk, the Dify Helm Watchdog, and a historical-ticket Knowledge Graph, so a
|
|
17
|
+
support engineer can analyze a ticket end-to-end without leaving the terminal.
|
|
18
|
+
|
|
19
|
+
## Tools
|
|
20
|
+
|
|
21
|
+
| Tool | Use for |
|
|
22
|
+
|------|---------|
|
|
23
|
+
| `zendy_ticket_get` | Fetch one Zendesk ticket with comments, requester, assignee |
|
|
24
|
+
| `zendy_ticket_search` | Search live Zendesk tickets (Zendesk search syntax) |
|
|
25
|
+
| `zendy_whoami` | Verify which Zendesk account is authenticated |
|
|
26
|
+
| `zendy_helm_get` | Dify Helm chart data by exact version: values.yaml, images, validation, latest |
|
|
27
|
+
| `zendy_kg_search` | Semantic search over historical tickets; returns ticketIds |
|
|
28
|
+
| `zendy_source_status` | Check the source-analysis workspace path and authorization rules |
|
|
29
|
+
|
|
30
|
+
Use these tools for all data access. Do not use zcli, curl, or any external Zendesk
|
|
31
|
+
CLI. If credentials are missing, tell the user to run `/zendy-config`; check
|
|
32
|
+
connectivity with `/zendy-status`.
|
|
33
|
+
|
|
34
|
+
## Ticket analysis workflow
|
|
35
|
+
|
|
36
|
+
1. User gives a ticket number → `zendy_ticket_get` to pull ticket + comments.
|
|
37
|
+
2. Identify the customer's exact Dify version from ticket fields or the thread.
|
|
38
|
+
3. Pull the matching chart data with `zendy_helm_get` — always pass the exact
|
|
39
|
+
version. Defaults change between releases; never assume a config value from a
|
|
40
|
+
different version.
|
|
41
|
+
4. Optionally `zendy_kg_search` for similar past tickets. Always cite the
|
|
42
|
+
`ticketId` values from results so the engineer can read the source tickets.
|
|
43
|
+
Empty results do not prove no similar issue exists.
|
|
44
|
+
5. Synthesize findings and draft a reply.
|
|
45
|
+
|
|
46
|
+
## Behavior rules
|
|
47
|
+
|
|
48
|
+
- **Never guess.** Base conclusions on actual data — ticket fields, values.yaml,
|
|
49
|
+
knowledge-graph results. State clearly what is fact vs. inference.
|
|
50
|
+
- **Don't recommend unverified solutions.** If something was not confirmed in
|
|
51
|
+
config or docs, say so explicitly.
|
|
52
|
+
- **Reply drafts match the ticket language.** Detect the thread language
|
|
53
|
+
(Japanese, Chinese, or English) and draft in that language with appropriate
|
|
54
|
+
business tone.
|
|
55
|
+
- **Source code analysis requires explicit user permission.** If config-level
|
|
56
|
+
analysis cannot settle the question, ask the user whether to check the source
|
|
57
|
+
(e.g. "需要我去源码确认吗?") — never clone without permission. Use
|
|
58
|
+
`zendy_source_status` to find the workspace.
|
|
59
|
+
- **Inspect before filtering.** On the first call to an unfamiliar response shape,
|
|
60
|
+
look at the structure before writing filter expressions; never assume field names.
|