@qlingzzy/qling 1.0.0 → 1.1.0

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.
Files changed (254) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.en.md +82 -17
  3. package/README.md +15 -7
  4. package/dist/agent/main-loop.d.ts +120 -0
  5. package/dist/agent/main-loop.d.ts.map +1 -0
  6. package/dist/agent/main-loop.js +279 -0
  7. package/dist/agent/subtask-parallel.d.ts +29 -0
  8. package/dist/agent/subtask-parallel.d.ts.map +1 -0
  9. package/dist/agent/subtask-parallel.js +107 -0
  10. package/dist/agent/subtask.d.ts +13 -0
  11. package/dist/agent/subtask.d.ts.map +1 -1
  12. package/dist/agent/subtask.js +79 -24
  13. package/dist/agent/system-prompt.d.ts +39 -0
  14. package/dist/agent/system-prompt.d.ts.map +1 -0
  15. package/dist/agent/system-prompt.js +89 -0
  16. package/dist/agent/tool-orchestrator.d.ts +66 -0
  17. package/dist/agent/tool-orchestrator.d.ts.map +1 -0
  18. package/dist/agent/tool-orchestrator.js +355 -0
  19. package/dist/agent-loop.d.ts +29 -16
  20. package/dist/agent-loop.d.ts.map +1 -1
  21. package/dist/agent-loop.js +262 -807
  22. package/dist/agents/roles.d.ts +57 -0
  23. package/dist/agents/roles.d.ts.map +1 -0
  24. package/dist/agents/roles.js +198 -0
  25. package/dist/commands/agents.d.ts.map +1 -1
  26. package/dist/commands/agents.js +17 -3
  27. package/dist/commands/claude-style.d.ts.map +1 -1
  28. package/dist/commands/claude-style.js +19 -10
  29. package/dist/commands/help.d.ts.map +1 -1
  30. package/dist/commands/help.js +3 -2
  31. package/dist/commands/index.d.ts.map +1 -1
  32. package/dist/commands/index.js +6 -0
  33. package/dist/commands/mode.d.ts +16 -0
  34. package/dist/commands/mode.d.ts.map +1 -0
  35. package/dist/commands/mode.js +67 -0
  36. package/dist/commands/recover.d.ts +3 -0
  37. package/dist/commands/recover.d.ts.map +1 -0
  38. package/dist/commands/recover.js +69 -0
  39. package/dist/commands/runtime.d.ts +2 -0
  40. package/dist/commands/runtime.d.ts.map +1 -1
  41. package/dist/commands/runtime.js +2 -0
  42. package/dist/commands/trace.d.ts +3 -0
  43. package/dist/commands/trace.d.ts.map +1 -0
  44. package/dist/commands/trace.js +45 -0
  45. package/dist/commands/verify.js +2 -2
  46. package/dist/config-report.d.ts +0 -1
  47. package/dist/config-report.d.ts.map +1 -1
  48. package/dist/config-report.js +1 -2
  49. package/dist/config.d.ts +0 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +0 -1
  52. package/dist/context-report.d.ts +13 -7
  53. package/dist/context-report.d.ts.map +1 -1
  54. package/dist/context-report.js +64 -61
  55. package/dist/context-tool-hygiene.d.ts +36 -0
  56. package/dist/context-tool-hygiene.d.ts.map +1 -0
  57. package/dist/context-tool-hygiene.js +125 -0
  58. package/dist/dashboard/client.d.ts +2 -0
  59. package/dist/dashboard/client.d.ts.map +1 -0
  60. package/dist/dashboard/client.js +305 -0
  61. package/dist/dashboard/model.d.ts +13 -0
  62. package/dist/dashboard/model.d.ts.map +1 -0
  63. package/dist/dashboard/model.js +112 -0
  64. package/dist/dashboard/page.d.ts +3 -0
  65. package/dist/dashboard/page.d.ts.map +1 -0
  66. package/dist/dashboard/page.js +204 -0
  67. package/dist/dashboard/types.d.ts +63 -0
  68. package/dist/dashboard/types.d.ts.map +1 -0
  69. package/dist/dashboard/types.js +1 -0
  70. package/dist/dashboard-server.d.ts +24 -4
  71. package/dist/dashboard-server.d.ts.map +1 -1
  72. package/dist/dashboard-server.js +397 -302
  73. package/dist/discovery-registry.d.ts +17 -3
  74. package/dist/discovery-registry.d.ts.map +1 -1
  75. package/dist/discovery-registry.js +78 -3
  76. package/dist/doctor.d.ts +8 -0
  77. package/dist/doctor.d.ts.map +1 -1
  78. package/dist/doctor.js +116 -0
  79. package/dist/eval/llm-tasks.d.ts +5 -0
  80. package/dist/eval/llm-tasks.d.ts.map +1 -0
  81. package/dist/eval/llm-tasks.js +125 -0
  82. package/dist/eval/repo-tasks.d.ts +12 -0
  83. package/dist/eval/repo-tasks.d.ts.map +1 -0
  84. package/dist/eval/repo-tasks.js +220 -0
  85. package/dist/eval/runner.d.ts +3 -1
  86. package/dist/eval/runner.d.ts.map +1 -1
  87. package/dist/eval/runner.js +23 -4
  88. package/dist/eval/tasks.d.ts.map +1 -1
  89. package/dist/eval/tasks.js +186 -0
  90. package/dist/eval/types.d.ts +8 -5
  91. package/dist/eval/types.d.ts.map +1 -1
  92. package/dist/execution/event-bus.d.ts +44 -0
  93. package/dist/execution/event-bus.d.ts.map +1 -0
  94. package/dist/execution/event-bus.js +76 -0
  95. package/dist/execution/failure-classifier.d.ts +11 -0
  96. package/dist/execution/failure-classifier.d.ts.map +1 -0
  97. package/dist/execution/failure-classifier.js +66 -0
  98. package/dist/execution/progress-detector.d.ts +3 -0
  99. package/dist/execution/progress-detector.d.ts.map +1 -0
  100. package/dist/execution/progress-detector.js +23 -0
  101. package/dist/execution/recovery-controller.d.ts +24 -0
  102. package/dist/execution/recovery-controller.d.ts.map +1 -0
  103. package/dist/execution/recovery-controller.js +114 -0
  104. package/dist/execution/recovery-messages.d.ts +24 -0
  105. package/dist/execution/recovery-messages.d.ts.map +1 -0
  106. package/dist/execution/recovery-messages.js +48 -0
  107. package/dist/execution/recovery-metrics.d.ts +16 -0
  108. package/dist/execution/recovery-metrics.d.ts.map +1 -0
  109. package/dist/execution/recovery-metrics.js +73 -0
  110. package/dist/execution/recovery-strategy-planner.d.ts +6 -0
  111. package/dist/execution/recovery-strategy-planner.d.ts.map +1 -0
  112. package/dist/execution/recovery-strategy-planner.js +24 -0
  113. package/dist/execution/run-trace-store.d.ts +29 -0
  114. package/dist/execution/run-trace-store.d.ts.map +1 -0
  115. package/dist/execution/run-trace-store.js +140 -0
  116. package/dist/execution/staged-verifier.d.ts +33 -0
  117. package/dist/execution/staged-verifier.d.ts.map +1 -0
  118. package/dist/execution/staged-verifier.js +39 -0
  119. package/dist/execution/types.d.ts +58 -0
  120. package/dist/execution/types.d.ts.map +1 -0
  121. package/dist/execution/types.js +1 -0
  122. package/dist/execution/verification-loop.d.ts +58 -0
  123. package/dist/execution/verification-loop.d.ts.map +1 -0
  124. package/dist/execution/verification-loop.js +195 -0
  125. package/dist/execution/verification-stages.d.ts +17 -0
  126. package/dist/execution/verification-stages.d.ts.map +1 -0
  127. package/dist/execution/verification-stages.js +102 -0
  128. package/dist/help-topics.d.ts.map +1 -1
  129. package/dist/help-topics.js +11 -0
  130. package/dist/index.js +0 -8
  131. package/dist/lsp/ts-service.d.ts +36 -0
  132. package/dist/lsp/ts-service.d.ts.map +1 -0
  133. package/dist/lsp/ts-service.js +291 -0
  134. package/dist/memory/lifecycle.d.ts +19 -0
  135. package/dist/memory/lifecycle.d.ts.map +1 -0
  136. package/dist/memory/lifecycle.js +53 -0
  137. package/dist/memory.d.ts +0 -16
  138. package/dist/memory.d.ts.map +1 -1
  139. package/dist/memory.js +0 -36
  140. package/dist/metrics/collector.d.ts +7 -0
  141. package/dist/metrics/collector.d.ts.map +1 -1
  142. package/dist/metrics/collector.js +60 -0
  143. package/dist/mission/manager.d.ts +1 -0
  144. package/dist/mission/manager.d.ts.map +1 -1
  145. package/dist/mission/manager.js +9 -0
  146. package/dist/mission/progress-notify.d.ts +39 -0
  147. package/dist/mission/progress-notify.d.ts.map +1 -0
  148. package/dist/mission/progress-notify.js +386 -0
  149. package/dist/pipeline/hooks.d.ts +1 -1
  150. package/dist/pipeline/hooks.d.ts.map +1 -1
  151. package/dist/pipeline/hooks.js +1 -0
  152. package/dist/pipeline/sections.d.ts +8 -4
  153. package/dist/pipeline/sections.d.ts.map +1 -1
  154. package/dist/pipeline/sections.js +70 -27
  155. package/dist/pipeline/verification.d.ts +1 -0
  156. package/dist/pipeline/verification.d.ts.map +1 -1
  157. package/dist/pipeline/verification.js +10 -7
  158. package/dist/providers/llm-client.d.ts +38 -0
  159. package/dist/providers/llm-client.d.ts.map +1 -0
  160. package/dist/providers/llm-client.js +105 -0
  161. package/dist/sdk.d.ts +1 -0
  162. package/dist/sdk.d.ts.map +1 -1
  163. package/dist/sdk.js +1 -0
  164. package/dist/session/durable-session-supervisor.d.ts.map +1 -1
  165. package/dist/session/durable-session-supervisor.js +13 -6
  166. package/dist/session/session-persistence.d.ts +29 -0
  167. package/dist/session/session-persistence.d.ts.map +1 -0
  168. package/dist/session/session-persistence.js +45 -0
  169. package/dist/session/session-scheduler.d.ts +8 -1
  170. package/dist/session/session-scheduler.d.ts.map +1 -1
  171. package/dist/session/session-scheduler.js +33 -4
  172. package/dist/session-task-report.d.ts +1 -0
  173. package/dist/session-task-report.d.ts.map +1 -1
  174. package/dist/session-task-report.js +2 -1
  175. package/dist/shortcuts.d.ts.map +1 -1
  176. package/dist/shortcuts.js +1 -0
  177. package/dist/skills/registry.d.ts.map +1 -1
  178. package/dist/skills/registry.js +10 -0
  179. package/dist/skills/security-scan.d.ts +15 -0
  180. package/dist/skills/security-scan.d.ts.map +1 -0
  181. package/dist/skills/security-scan.js +95 -0
  182. package/dist/skills/types.d.ts +2 -0
  183. package/dist/skills/types.d.ts.map +1 -1
  184. package/dist/statusline.d.ts +3 -3
  185. package/dist/statusline.d.ts.map +1 -1
  186. package/dist/statusline.js +9 -17
  187. package/dist/token-usage.d.ts +27 -0
  188. package/dist/token-usage.d.ts.map +1 -0
  189. package/dist/token-usage.js +107 -0
  190. package/dist/tools/bash.d.ts.map +1 -1
  191. package/dist/tools/bash.js +5 -3
  192. package/dist/tools/browser-act-session.d.ts +48 -0
  193. package/dist/tools/browser-act-session.d.ts.map +1 -0
  194. package/dist/tools/browser-act-session.js +168 -0
  195. package/dist/tools/browser-act.d.ts +14 -0
  196. package/dist/tools/browser-act.d.ts.map +1 -0
  197. package/dist/tools/browser-act.js +297 -0
  198. package/dist/tools/code-symbols.d.ts +35 -0
  199. package/dist/tools/code-symbols.d.ts.map +1 -0
  200. package/dist/tools/code-symbols.js +257 -0
  201. package/dist/tools/index.d.ts +4 -1
  202. package/dist/tools/index.d.ts.map +1 -1
  203. package/dist/tools/index.js +22 -2
  204. package/dist/tools/lsp.d.ts +13 -0
  205. package/dist/tools/lsp.d.ts.map +1 -0
  206. package/dist/tools/lsp.js +136 -0
  207. package/dist/tools/patch.d.ts +5 -0
  208. package/dist/tools/patch.d.ts.map +1 -1
  209. package/dist/tools/patch.js +25 -4
  210. package/dist/tools/search.d.ts +7 -0
  211. package/dist/tools/search.d.ts.map +1 -1
  212. package/dist/tools/search.js +20 -3
  213. package/dist/tools/skill.d.ts +2 -0
  214. package/dist/tools/skill.d.ts.map +1 -1
  215. package/dist/tools/skill.js +47 -14
  216. package/dist/tools/subtask.d.ts +3 -1
  217. package/dist/tools/subtask.d.ts.map +1 -1
  218. package/dist/tools/subtask.js +151 -56
  219. package/dist/tui/shell.d.ts +12 -0
  220. package/dist/tui/shell.d.ts.map +1 -1
  221. package/dist/tui/shell.js +36 -3
  222. package/dist/tui/streaming-repl.d.ts +1 -0
  223. package/dist/tui/streaming-repl.d.ts.map +1 -1
  224. package/dist/tui/streaming-repl.js +27 -4
  225. package/dist/tui/streaming-tui.d.ts +10 -0
  226. package/dist/tui/streaming-tui.d.ts.map +1 -1
  227. package/dist/tui/streaming-tui.js +70 -12
  228. package/dist/types.d.ts +0 -8
  229. package/dist/types.d.ts.map +1 -1
  230. package/dist/workflow-runtime.d.ts.map +1 -1
  231. package/dist/workflow-runtime.js +10 -2
  232. package/dist/workflow-types.d.ts +2 -0
  233. package/dist/workflow-types.d.ts.map +1 -1
  234. package/docs/install.md +29 -8
  235. package/docs/skills.md +56 -7
  236. package/docs/web-routing.md +83 -0
  237. package/package.json +18 -4
  238. package/scripts/dep-layers.mjs +275 -0
  239. package/scripts/eval-llm.mjs +27 -0
  240. package/scripts/eval-recovery.mjs +101 -0
  241. package/scripts/eval-tasks.mjs +20 -0
  242. package/scripts/validate-packaging.mjs +51 -0
  243. package/skills/examples/README.md +23 -0
  244. package/skills/examples/add-function/SKILL.md +31 -0
  245. package/skills/examples/fix-failing-test/SKILL.md +32 -0
  246. package/skills/examples/pr-summary/SKILL.md +34 -0
  247. package/skills/lifecycle-build/SKILL.md +22 -0
  248. package/skills/lifecycle-plan/SKILL.md +32 -0
  249. package/skills/lifecycle-review/SKILL.md +25 -0
  250. package/skills/lifecycle-ship/SKILL.md +22 -0
  251. package/skills/lifecycle-spec/SKILL.md +24 -0
  252. package/skills/lifecycle-test/SKILL.md +21 -0
  253. package/skills/opencli/SKILL.md +222 -0
  254. package/skills/qling.md +14 -3
@@ -0,0 +1,136 @@
1
+ // ============================================================
2
+ // Phase 4.3 — lsp 工具(可选 TS 语义查询)
3
+ // QLING_LSP=1 启用
4
+ // ============================================================
5
+ import { toolError, toolSuccess } from "./error-utils.js";
6
+ import { getRuntimeRootsFromEnv, isWithinAllowedRoots } from "../runtime-paths.js";
7
+ import { getOrCreateServiceForFile, isLspEnabled, loadTypeScript, lspDefinition, lspDocumentSymbols, lspHover, lspReferences, resetTsServiceCache, resolveAbsFile, } from "../lsp/ts-service.js";
8
+ export { isLspEnabled, resetTsServiceCache };
9
+ export const lspTool = {
10
+ name: "lsp",
11
+ description: "Optional TypeScript semantic queries (definition/hover/references/document_symbols). DISABLED by default — set QLING_LSP=1. Uses in-process TypeScript LanguageService (not full multi-language LSP).",
12
+ longDescription: `可选 TypeScript 语义查询(进程内 LanguageService)。
13
+
14
+ **启用**: \`QLING_LSP=1\`(且环境可 resolve \`typescript\` 包)
15
+
16
+ **action**:
17
+ - definition — 跳转到定义(path + line + character,1-based)
18
+ - hover — 类型/文档提示
19
+ - references — 查找引用
20
+ - document_symbols — 当前文件符号大纲
21
+
22
+ **限制**: 当前仅 TS/JS 系;非 LSP 多语言协议客户端。通用符号搜索请用 code_symbols。`,
23
+ parameters: {
24
+ type: "object",
25
+ properties: {
26
+ action: {
27
+ type: "string",
28
+ description: "definition | hover | references | document_symbols",
29
+ },
30
+ path: {
31
+ type: "string",
32
+ description: "File path relative to workspace or absolute",
33
+ },
34
+ line: {
35
+ type: "number",
36
+ description: "1-based line number",
37
+ },
38
+ character: {
39
+ type: "number",
40
+ description: "1-based column (character offset in line)",
41
+ },
42
+ limit: {
43
+ type: "number",
44
+ description: "Max results for references/symbols (default 30/80)",
45
+ },
46
+ },
47
+ required: ["action", "path"],
48
+ },
49
+ scenes: ["code", "navigation"],
50
+ priority: 7,
51
+ readOnly: true,
52
+ destructive: false,
53
+ concurrencySafe: true,
54
+ effortHint: "low",
55
+ };
56
+ function parseLineChar(args) {
57
+ const line = Number(args.line);
58
+ const character = Number(args.character ?? 1);
59
+ return {
60
+ line: Number.isFinite(line) && line >= 1 ? Math.floor(line) : 1,
61
+ character: Number.isFinite(character) && character >= 1 ? Math.floor(character) : 1,
62
+ };
63
+ }
64
+ const MAX_LSP_RESULTS = 200;
65
+ export function clampLspLimit(raw, fallback) {
66
+ const parsed = Number(raw);
67
+ if (!Number.isFinite(parsed) || parsed <= 0)
68
+ return fallback;
69
+ return Math.min(MAX_LSP_RESULTS, Math.floor(parsed));
70
+ }
71
+ export async function runLsp(args) {
72
+ if (!isLspEnabled()) {
73
+ return toolError("LSP_DISABLED", "lsp 默认关闭。启用: QLING_LSP=1(需可加载 typescript)。通用符号搜索用 code_symbols。", { category: "permission" });
74
+ }
75
+ const action = String(args.action ?? "").trim().toLowerCase();
76
+ const allowed = ["definition", "hover", "references", "document_symbols", "symbols"];
77
+ if (!allowed.includes(action)) {
78
+ return toolError("LSP_INVALID_ACTION", `action must be one of: definition, hover, references, document_symbols`);
79
+ }
80
+ const filePath = String(args.path ?? "").trim();
81
+ if (!filePath) {
82
+ return toolError("LSP_MISSING_PATH", "path is required");
83
+ }
84
+ const ts = await loadTypeScript();
85
+ if (!ts) {
86
+ return toolError("LSP_TYPESCRIPT_MISSING", "无法加载 typescript 包。请在项目中安装 typescript(devDependency)后重试。");
87
+ }
88
+ const roots = getRuntimeRootsFromEnv();
89
+ const workspaceDir = roots.workspaceDir ?? process.cwd();
90
+ const abs = resolveAbsFile(workspaceDir, filePath);
91
+ const rootsWithWorkspace = { ...roots, workspaceDir };
92
+ if (!isWithinAllowedRoots(abs, rootsWithWorkspace)) {
93
+ return toolError("LSP_PATH_OUTSIDE_ROOTS", `拒绝读取 runtime roots 外文件: ${filePath}`, { category: "permission" });
94
+ }
95
+ try {
96
+ const bundle = getOrCreateServiceForFile(ts, workspaceDir, abs);
97
+ const { line, character } = parseLineChar(args);
98
+ if (action === "definition") {
99
+ const locs = lspDefinition(bundle, abs, line, character);
100
+ if (locs.length === 0) {
101
+ return toolSuccess(`lsp definition: (none) at ${filePath}:${line}:${character}`);
102
+ }
103
+ const lines = locs.map((l) => `${l.file}:${l.line}:${l.character}` +
104
+ (l.preview ? ` ${l.preview}` : ""));
105
+ return toolSuccess(`lsp definition (${locs.length}):\n${lines.join("\n")}`);
106
+ }
107
+ if (action === "hover") {
108
+ const h = lspHover(bundle, abs, line, character);
109
+ if (!h) {
110
+ return toolSuccess(`lsp hover: (none) at ${filePath}:${line}:${character}`);
111
+ }
112
+ return toolSuccess(`lsp hover @ ${filePath}:${line}:${character}\n${h.display}` +
113
+ (h.documentation ? `\n\n${h.documentation}` : ""));
114
+ }
115
+ if (action === "references") {
116
+ const limit = clampLspLimit(args.limit, 30);
117
+ const locs = lspReferences(bundle, abs, line, character, limit);
118
+ if (locs.length === 0) {
119
+ return toolSuccess(`lsp references: (none)`);
120
+ }
121
+ const lines = locs.map((l) => `${l.file}:${l.line}:${l.character}`);
122
+ return toolSuccess(`lsp references (${locs.length}):\n${lines.join("\n")}`);
123
+ }
124
+ // document_symbols | symbols
125
+ const limit = clampLspLimit(args.limit, 80);
126
+ const syms = lspDocumentSymbols(bundle, abs, limit);
127
+ if (syms.length === 0) {
128
+ return toolSuccess(`lsp document_symbols: (none) in ${filePath}`);
129
+ }
130
+ const lines = syms.map((s) => `${s.line}:${s.character} [${s.kind}] ${s.name}`);
131
+ return toolSuccess(`lsp document_symbols (${syms.length}) ${filePath}:\n${lines.join("\n")}`);
132
+ }
133
+ catch (err) {
134
+ return toolError("LSP_FAILED", err instanceof Error ? err.message : String(err), { category: "runtime" });
135
+ }
136
+ }
@@ -17,6 +17,11 @@ export declare function runPatch(args: {
17
17
  chunks: PatchChunk[];
18
18
  dry_run?: boolean;
19
19
  }): Promise<ToolResult>;
20
+ /**
21
+ * Write via temp file then rename. Falls back to direct write on platforms
22
+ * where rename cannot replace an existing target (common on Windows).
23
+ */
24
+ export declare function writeFileAtomic(targetPath: string, content: string): Promise<void>;
20
25
  export declare function generateUnifiedDiff(filePath: string, originalContent: string, newContent: string): string;
21
26
  export {};
22
27
  //# sourceMappingURL=patch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASzD,yCAAyC;AACzC,eAAO,MAAM,oBAAoB,QAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,EAAE,cA+EvB,CAAC;AAEF,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAmC5F;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,UAAU,CAAC,CA4HtB;AAkID,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,GACjB,MAAM,CA6IR"}
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASzD,yCAAyC;AACzC,eAAO,MAAM,oBAAoB,QAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,EAAE,cA+EvB,CAAC;AAEF,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAmC5F;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,UAAU,CAAC,CA4HtB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBxF;AAkID,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,GACjB,MAAM,CA6IR"}
@@ -1,5 +1,6 @@
1
- import { readFile, writeFile } from "fs/promises";
2
- import { relative } from "path";
1
+ import { randomBytes } from "crypto";
2
+ import { readFile, rename, unlink, writeFile } from "fs/promises";
3
+ import { basename, dirname, join, relative } from "path";
3
4
  import { getErrorMessage, toolError, toolSuccess } from "./error-utils.js";
4
5
  import { checkSensitiveWriteTarget, getRuntimeRootsFromEnv, isPathAllowedForWrite, resolveToolPath, } from "../runtime-paths.js";
5
6
  /** 超过该字节数拒绝做 LCS diff / 写入,防止超大文件 OOM */
@@ -205,9 +206,9 @@ export async function runPatch(args) {
205
206
  return toolSuccess(`🔎 dry_run OK — would apply ${chunks.length} patch chunk(s) to ${resolvedPath}\n` +
206
207
  `${summary}\n\n${diffText}\n\n(no file written)`);
207
208
  }
208
- // Write changes after all validations pass
209
+ // Atomic write after all validations pass (temp + rename; Windows-safe fallback)
209
210
  try {
210
- await writeFile(resolvedPath, currentContent, "utf-8");
211
+ await writeFileAtomic(resolvedPath, currentContent);
211
212
  return toolSuccess(`✅ Successfully applied ${chunks.length} patch chunk(s) to ${resolvedPath}\n` +
212
213
  `${summary}\n\n${diffText}`);
213
214
  }
@@ -215,6 +216,26 @@ export async function runPatch(args) {
215
216
  return toolError("PATCH_WRITE_FAILED", `failed to write file: ${getErrorMessage(err)}`);
216
217
  }
217
218
  }
219
+ /**
220
+ * Write via temp file then rename. Falls back to direct write on platforms
221
+ * where rename cannot replace an existing target (common on Windows).
222
+ */
223
+ export async function writeFileAtomic(targetPath, content) {
224
+ const dir = dirname(targetPath);
225
+ const tmp = join(dir, `.qling-patch-${basename(targetPath)}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
226
+ await writeFile(tmp, content, "utf-8");
227
+ try {
228
+ await rename(tmp, targetPath);
229
+ }
230
+ catch {
231
+ try {
232
+ await writeFile(targetPath, content, "utf-8");
233
+ }
234
+ finally {
235
+ await unlink(tmp).catch(() => undefined);
236
+ }
237
+ }
238
+ }
218
239
  function countOccurrences(text, searchStr) {
219
240
  if (!searchStr)
220
241
  return 0;
@@ -1,4 +1,9 @@
1
1
  import { ToolDefinition, ToolResult } from "../types.js";
2
+ /** Sprint 3: 默认更紧的结果预算,减少上下文膨胀 */
3
+ export declare const SEARCH_DEFAULT_LIMIT = 40;
4
+ export declare const SEARCH_MAX_LIMIT = 200;
5
+ /** 单行匹配展示上限(字符,含路径前缀后) */
6
+ export declare const SEARCH_MAX_LINE_CHARS = 240;
2
7
  export declare const searchTool: ToolDefinition;
3
8
  export declare function runSearch(args: {
4
9
  pattern: string;
@@ -8,4 +13,6 @@ export declare function runSearch(args: {
8
13
  context?: number;
9
14
  limit?: number;
10
15
  }): Promise<ToolResult>;
16
+ /** Truncate long match lines so search results stay harness-friendly. */
17
+ export declare function truncateSearchLine(line: string, maxChars?: number): string;
11
18
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAqBzD,eAAO,MAAM,UAAU,EAAE,cAgGxB,CAAC;AAEF,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,UAAU,CAAC,CAkDtB"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKzD,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,0BAA0B;AAC1B,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAiBzC,eAAO,MAAM,UAAU,EAAE,cAgGxB,CAAC;AAEF,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmDtB;AA6hBD,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAwB,GAAG,MAAM,CAIzF"}
@@ -10,6 +10,11 @@ import { execFile } from "child_process";
10
10
  import { getErrorMessage, toolError, toolSuccess } from "./error-utils.js";
11
11
  import { getRuntimeRootsFromEnv, isWithinAllowedRoots, resolveToolPath } from "../runtime-paths.js";
12
12
  const MAX_SEARCH_FILE_BYTES = 2 * 1024 * 1024; // 2MB
13
+ /** Sprint 3: 默认更紧的结果预算,减少上下文膨胀 */
14
+ export const SEARCH_DEFAULT_LIMIT = 40;
15
+ export const SEARCH_MAX_LIMIT = 200;
16
+ /** 单行匹配展示上限(字符,含路径前缀后) */
17
+ export const SEARCH_MAX_LINE_CHARS = 240;
13
18
  const DEFAULT_IGNORES = new Set([
14
19
  "node_modules",
15
20
  ".git",
@@ -132,7 +137,8 @@ export async function runSearch(args) {
132
137
  return toolError("SEARCH_INVALID_TARGET", `unsupported target: ${target}`);
133
138
  }
134
139
  const context = clamp(args.context ?? 0, 0, 10);
135
- const limit = clamp(args.limit ?? 50, 1, 200);
140
+ // Sprint 3: tighter limit 40;单行过长截断,避免冲垮上下文
141
+ const limit = clamp(args.limit ?? SEARCH_DEFAULT_LIMIT, 1, SEARCH_MAX_LIMIT);
136
142
  const absPath = resolveToolPath(searchPath, roots, "workspace");
137
143
  if (!isWithinAllowedRoots(absPath, roots)) {
138
144
  return toolError("SEARCH_OUTSIDE_ALLOWED_ROOT", `${absPath} is outside allowed roots`);
@@ -239,7 +245,9 @@ async function searchWithRipgrep(absPath, pattern, target, fileGlob, context, li
239
245
  const truncated = parsedLines.length > limit;
240
246
  const sliced = parsedLines.slice(0, limit);
241
247
  const suffix = truncated ? `\n... (truncated at ${limit} results)` : "";
242
- const formatted = sliced.map(pl => `${pl.file}:${pl.line}:${pl.content}`).join("\n");
248
+ const formatted = sliced
249
+ .map((pl) => truncateSearchLine(`${pl.file}:${pl.line}:${pl.content}`))
250
+ .join("\n");
243
251
  resolve(toolSuccess(`${truncated ? `${limit}+` : parsedLines.length} match(es):\n${formatted}${suffix}`));
244
252
  return;
245
253
  }
@@ -399,7 +407,9 @@ async function searchWithGitGrep(absPath, pattern, target, fileGlob, context, li
399
407
  const truncated = parsedLines.length > limit;
400
408
  const sliced = parsedLines.slice(0, limit);
401
409
  const suffix = truncated ? `\n... (truncated at ${limit} results)` : "";
402
- const formatted = sliced.map(pl => `${pl.file}:${pl.line}:${pl.content}`).join("\n");
410
+ const formatted = sliced
411
+ .map((pl) => truncateSearchLine(`${pl.file}:${pl.line}:${pl.content}`))
412
+ .join("\n");
403
413
  resolve(toolSuccess(`${truncated ? `${limit}+` : parsedLines.length} match(es):\n${formatted}${suffix}`));
404
414
  return;
405
415
  }
@@ -631,6 +641,13 @@ function buildRegex(pattern, flags = "") {
631
641
  function escapeRegExp(char) {
632
642
  return char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
633
643
  }
644
+ /** Truncate long match lines so search results stay harness-friendly. */
645
+ export function truncateSearchLine(line, maxChars = SEARCH_MAX_LINE_CHARS) {
646
+ const text = String(line ?? "");
647
+ if (text.length <= maxChars)
648
+ return text;
649
+ return text.slice(0, Math.max(0, maxChars - 1)) + "…";
650
+ }
634
651
  function clamp(value, min, max) {
635
652
  return Math.max(min, Math.min(max, value));
636
653
  }
@@ -1,4 +1,6 @@
1
1
  import { ToolDefinition, ToolResult } from "../types.js";
2
+ /** 包根目录(dist/tools/skill.js → 仓库或 npm 包根,含 skills/) */
3
+ export declare function getPackageRootForSkills(): string;
2
4
  export declare function getSkillDirs(): string[];
3
5
  export declare const skillTool: ToolDefinition;
4
6
  export declare function runSkill(args: {
@@ -1 +1 @@
1
- {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../src/tools/skill.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzD,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAUvC;AAiCD,eAAO,MAAM,SAAS,EAAE,cAmEvB,CAAC;AAEF,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CA4D3F"}
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../src/tools/skill.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzD,uDAAuD;AACvD,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAMD,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAuBvC;AAiCD,eAAO,MAAM,SAAS,EAAE,cAmEvB,CAAC;AAEF,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CA0E3F"}
@@ -5,21 +5,43 @@
5
5
  // ============================================================
6
6
  import { readFile } from "fs/promises";
7
7
  import { existsSync } from "fs";
8
- import { resolve, join } from "path";
8
+ import { homedir } from "os";
9
+ import { resolve, join, dirname } from "path";
10
+ import { fileURLToPath } from "url";
9
11
  import { toolError, toolSuccess } from "./error-utils.js";
10
12
  import { listSkills, searchSkills } from "../skills/registry.js";
13
+ import { formatSkillScanBlockMessage, scanSkillContent, } from "../skills/security-scan.js";
14
+ /** 包根目录(dist/tools/skill.js → 仓库或 npm 包根,含 skills/) */
15
+ export function getPackageRootForSkills() {
16
+ const here = dirname(fileURLToPath(import.meta.url));
17
+ // dist/tools → ../.. = package root
18
+ return resolve(here, "..", "..");
19
+ }
11
20
  // Skill 查找路径
12
21
  // 支持两种格式:
13
22
  // name="foo" → 搜索 skills/foo.md
14
23
  // name="@scope/foo" → 搜索 skills/scopes/scope/foo.md
15
24
  export function getSkillDirs() {
16
- const dirs = [
17
- resolve(process.cwd(), "skills"),
18
- resolve(process.cwd(), ".qling/skills"),
19
- ];
25
+ const dirs = [];
26
+ const seen = new Set();
27
+ const add = (p) => {
28
+ const abs = resolve(p);
29
+ if (seen.has(abs))
30
+ return;
31
+ seen.add(abs);
32
+ dirs.push(abs);
33
+ };
34
+ // 1) 随包发布的 skills(全局 npm 安装仍可用)
35
+ add(join(getPackageRootForSkills(), "skills"));
36
+ // 2) 用户全局
37
+ add(join(homedir(), ".qling", "skills"));
38
+ // 3) 当前工作区
39
+ add(resolve(process.cwd(), "skills"));
40
+ add(resolve(process.cwd(), ".qling", "skills"));
41
+ // 4) 可选 Hermes 兼容路径
20
42
  const hermesHome = process.env.HERMES_HOME;
21
43
  if (hermesHome && hermesHome.trim()) {
22
- dirs.push(join(hermesHome.trim(), "skills"));
44
+ add(join(hermesHome.trim(), "skills"));
23
45
  }
24
46
  return dirs;
25
47
  }
@@ -58,26 +80,26 @@ function resolveSkillPath(name) {
58
80
  }
59
81
  export const skillTool = {
60
82
  name: "skill",
61
- description: "Dynamically load a skill/knowledge file (.md). Use when encountering unfamiliar tools, APIs, or frameworks. Use 'list' to discover available skills, 'search query=X' to find skills, or 'name=X' to load one.",
83
+ description: "Load a skill knowledge file. Use for unfamiliar tools/APIs and ALWAYS before opencli/social platforms (Douyin, Xiaohongshu, Weibo, Bilibili, TikTok, Twitter). Actions: list | search query= | name=opencli.",
62
84
  longDescription: `动态加载技能/知识文件(SKILL.md),内容通过 tool_result 注入上下文。
63
85
 
64
86
  **核心原则**: "用到什么知识,临时加载什么知识,不塞 system prompt"
65
87
 
66
88
  **三种操作**:
67
89
  - skill list — 列出所有可用技能
68
- - skill search query="docker" — 搜索技能
69
- - skill name="docker" — 加载技能内容
90
+ - skill search query="opencli" — 搜索技能
91
+ - skill name="opencli" — 加载 opencli 调用手册(抖音/小红书等)
70
92
 
71
93
  **查找路径**:
72
- - skills/{name}.md
73
- - skills/{category}/{name}.md
94
+ - 包内 skills/(npm 全局安装可用)
95
+ - ~/.qling/skills/
96
+ - 工作区 skills/ 与 .qling/skills/
74
97
  - @scope/name → scopes/{scope}/{name}.md
75
98
 
76
99
  **使用场景**:
100
+ - 抖音/小红书/微博/B站/TikTok/推特 → skill(name="opencli") 再用 bash 跑 opencli
77
101
  - 调用 docker 但不熟悉命令 → skill(name="docker")
78
- - 调用 Kubernetes API → skill(name="k8s-debug")
79
102
  - 不确定有哪些技能 → skill list
80
- - 搜索特定领域技能 → skill search query="api"
81
103
 
82
104
  **返回内容**:
83
105
  - 文件的 markdown body(不含 frontmatter)
@@ -167,13 +189,24 @@ export async function runSkill(args) {
167
189
  }
168
190
  try {
169
191
  const content = await readFile(filePath, "utf-8");
192
+ const scan = scanSkillContent(content);
193
+ if (!scan.ok) {
194
+ return toolError("SKILL_SECURITY_BLOCKED", formatSkillScanBlockMessage(name, scan));
195
+ }
170
196
  const lines = content.split("\n");
171
197
  const startIdx = lines.findIndex((l) => l.trim() === "---");
172
198
  const endIdx = startIdx >= 0 ? lines.findIndex((l, i) => i > startIdx && l.trim() === "---") : -1;
173
199
  const body = startIdx >= 0 && endIdx >= 0
174
200
  ? lines.slice(endIdx + 1).join("\n").trim()
175
201
  : content;
176
- return toolSuccess(`📖 Skill: ${name}\n\n${body}`);
202
+ let prefix = `📖 Skill: ${name}\n\n`;
203
+ if (scan.findings.length > 0 && scan.mode === "warn") {
204
+ prefix +=
205
+ `⚠️ 安全扫描警告(仍加载):\n` +
206
+ scan.findings.map((f) => ` - [${f.severity}] ${f.rule}: ${f.detail}`).join("\n") +
207
+ "\n\n";
208
+ }
209
+ return toolSuccess(`${prefix}${body}`);
177
210
  }
178
211
  catch (err) {
179
212
  return toolError("SKILL_READ_FAILED", `failed to read skill: ${err.message}`);
@@ -1,8 +1,10 @@
1
1
  import type { ToolDefinition, ToolResult } from "../types.js";
2
2
  export declare const subtaskTool: ToolDefinition;
3
3
  export declare function runSubtask(args: {
4
- task: string;
4
+ task?: string;
5
+ tasks?: unknown;
5
6
  context?: string;
7
+ role?: string;
6
8
  max_iterations?: number;
7
9
  timeout_ms?: number;
8
10
  }): Promise<ToolResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"subtask.d.ts","sourceRoot":"","sources":["../../src/tools/subtask.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAa9D,eAAO,MAAM,WAAW,EAAE,cAyBzB,CAAC;AAEF,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAwFtB"}
1
+ {"version":3,"file":"subtask.d.ts","sourceRoot":"","sources":["../../src/tools/subtask.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0C9D,eAAO,MAAM,WAAW,EAAE,cAkDzB,CAAC;AAgDF,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,UAAU,CAAC,CAsHtB"}