@ken-jo/agent-connector 0.4.0 → 0.4.6

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 (185) hide show
  1. package/README.md +9 -9
  2. package/dist/{action-PLZW5BGX.js → action-EV63GAPY.js} +11 -10
  3. package/dist/{action-PLZW5BGX.js.map → action-EV63GAPY.js.map} +1 -1
  4. package/dist/amazon-q-COAL3764.js +276 -0
  5. package/dist/amazon-q-COAL3764.js.map +1 -0
  6. package/dist/amp-XJQ4TDAU.js +666 -0
  7. package/dist/amp-XJQ4TDAU.js.map +1 -0
  8. package/dist/{antigravity-ANDKF3K7.js → antigravity-AMKKKHS4.js} +4 -4
  9. package/dist/{antigravity-cli-LLRPDDOT.js → antigravity-cli-XERXEH5W.js} +4 -4
  10. package/dist/{chunk-MAH6QQTO.js → chunk-2X4EH2D7.js} +5 -4
  11. package/dist/chunk-2X4EH2D7.js.map +1 -0
  12. package/dist/{chunk-G3ER4SY5.js → chunk-5G4EPJ4Y.js} +9 -9
  13. package/dist/chunk-BMD7ANKF.js +237 -0
  14. package/dist/chunk-BMD7ANKF.js.map +1 -0
  15. package/dist/{chunk-IW77FFNX.js → chunk-CPLK7GTE.js} +41 -5
  16. package/dist/chunk-CPLK7GTE.js.map +1 -0
  17. package/dist/{chunk-ADTHT2FN.js → chunk-DCK5FADX.js} +10 -173
  18. package/dist/chunk-DCK5FADX.js.map +1 -0
  19. package/dist/{chunk-J3D5W5T2.js → chunk-EROJDXMW.js} +2 -2
  20. package/dist/{chunk-22Y24V7O.js → chunk-F5RX62T7.js} +2 -2
  21. package/dist/chunk-FAHIMJRU.js +177 -0
  22. package/dist/chunk-FAHIMJRU.js.map +1 -0
  23. package/dist/{chunk-BHUCB6LY.js → chunk-H67V7DEL.js} +8 -7
  24. package/dist/{chunk-BHUCB6LY.js.map → chunk-H67V7DEL.js.map} +1 -1
  25. package/dist/{chunk-BXGFUENQ.js → chunk-I5MLE6S7.js} +14 -1
  26. package/dist/{chunk-BXGFUENQ.js.map → chunk-I5MLE6S7.js.map} +1 -1
  27. package/dist/{chunk-LL4OBGRU.js → chunk-LQRAPXS4.js} +3 -3
  28. package/dist/{chunk-KGJWE6A2.js → chunk-MZF2YCXX.js} +2 -2
  29. package/dist/{chunk-KGXDYGNJ.js → chunk-QC5A3TFQ.js} +19 -19
  30. package/dist/{chunk-KGXDYGNJ.js.map → chunk-QC5A3TFQ.js.map} +1 -1
  31. package/dist/{chunk-CIRDOY2V.js → chunk-QOGMDOUX.js} +3 -3
  32. package/dist/{chunk-KGGBNKI4.js → chunk-RQPKL62M.js} +144 -21
  33. package/dist/chunk-RQPKL62M.js.map +1 -0
  34. package/dist/{chunk-6XHB6BE3.js → chunk-SCVBYSF7.js} +1 -1
  35. package/dist/{chunk-76FUSVIA.js → chunk-SRVAA2VN.js} +6 -5
  36. package/dist/chunk-SRVAA2VN.js.map +1 -0
  37. package/dist/chunk-WZA4CSGJ.js +28 -0
  38. package/dist/chunk-WZA4CSGJ.js.map +1 -0
  39. package/dist/{claude-code-MLWS7CW6.js → claude-code-T2LPFNC3.js} +20 -30
  40. package/dist/claude-code-T2LPFNC3.js.map +1 -0
  41. package/dist/cli/sdk.js +5 -4
  42. package/dist/cli/sdk.js.map +1 -1
  43. package/dist/cli.js +1 -1
  44. package/dist/cline-WRYMNS7Z.js +442 -0
  45. package/dist/cline-WRYMNS7Z.js.map +1 -0
  46. package/dist/{codebuff-SH2S3QAN.js → codebuff-PODYPOOP.js} +130 -5
  47. package/dist/codebuff-PODYPOOP.js.map +1 -0
  48. package/dist/{codex-BXA4FIF4.js → codex-O4G7D7KK.js} +61 -13
  49. package/dist/codex-O4G7D7KK.js.map +1 -0
  50. package/dist/continue-K43SUHLQ.js +790 -0
  51. package/dist/continue-K43SUHLQ.js.map +1 -0
  52. package/dist/{copilot-cli-ROIBTTDT.js → copilot-cli-Q3SSPT4V.js} +70 -12
  53. package/dist/copilot-cli-Q3SSPT4V.js.map +1 -0
  54. package/dist/{crush-QLEFLHUC.js → crush-MBEH2R3I.js} +130 -6
  55. package/dist/crush-MBEH2R3I.js.map +1 -0
  56. package/dist/{cursor-ASJPWZ4R.js → cursor-TMBXDYR4.js} +119 -19
  57. package/dist/cursor-TMBXDYR4.js.map +1 -0
  58. package/dist/{detect-ZIIS55LD.js → detect-AHJIUMBO.js} +5 -4
  59. package/dist/{detect-ZIIS55LD.js.map → detect-AHJIUMBO.js.map} +1 -1
  60. package/dist/{doctor-TJUGSQ5C.js → doctor-G5MLCPAB.js} +221 -95
  61. package/dist/doctor-G5MLCPAB.js.map +1 -0
  62. package/dist/{droid-NCSS3B5Y.js → droid-JC2NT6S4.js} +135 -13
  63. package/dist/droid-JC2NT6S4.js.map +1 -0
  64. package/dist/{gemini-cli-SLVWLXJW.js → gemini-cli-ILR7M5X3.js} +62 -10
  65. package/dist/gemini-cli-ILR7M5X3.js.map +1 -0
  66. package/dist/{goose-PBA7KTE5.js → goose-SFO5BBGW.js} +15 -8
  67. package/dist/goose-SFO5BBGW.js.map +1 -0
  68. package/dist/{hermes-ONDCECP2.js → hermes-3N7WN4WS.js} +329 -15
  69. package/dist/hermes-3N7WN4WS.js.map +1 -0
  70. package/dist/{hook-OU522ABV.js → hook-76JBKTUX.js} +14 -11
  71. package/dist/hook-76JBKTUX.js.map +1 -0
  72. package/dist/index.d.ts +2 -2
  73. package/dist/index.js +3 -2
  74. package/dist/{install-UDCBL7HP.js → install-R7252QFH.js} +11 -10
  75. package/dist/{install-UDCBL7HP.js.map → install-R7252QFH.js.map} +1 -1
  76. package/dist/{introspect-DuZSPSHB.d.ts → introspect-Cn3MSA4_.d.ts} +1 -1
  77. package/dist/{jetbrains-copilot-YABZ3HFA.js → jetbrains-copilot-NRZHI5PC.js} +85 -18
  78. package/dist/jetbrains-copilot-NRZHI5PC.js.map +1 -0
  79. package/dist/{kilo-F3ZDZKCT.js → kilo-IX4T3OHJ.js} +115 -19
  80. package/dist/kilo-IX4T3OHJ.js.map +1 -0
  81. package/dist/{kilo-cli-3HWPWBY4.js → kilo-cli-2AYCXBQD.js} +109 -13
  82. package/dist/kilo-cli-2AYCXBQD.js.map +1 -0
  83. package/dist/{kimi-NV72FVAO.js → kimi-2YFK2GBA.js} +87 -25
  84. package/dist/kimi-2YFK2GBA.js.map +1 -0
  85. package/dist/{kiro-C5W6AGDR.js → kiro-YL6AWUYG.js} +3 -3
  86. package/dist/{leaderboard-TIBVONVT.js → leaderboard-V6VWA5ZV.js} +3 -3
  87. package/dist/{mimo-code-IMM5YM3M.js → mimo-code-FTMGSKMQ.js} +41 -5
  88. package/dist/mimo-code-FTMGSKMQ.js.map +1 -0
  89. package/dist/{mux-DSSH4AFS.js → mux-5XZFB44C.js} +156 -6
  90. package/dist/mux-5XZFB44C.js.map +1 -0
  91. package/dist/{nemoclaw-3EVNKD3I.js → nemoclaw-CQYXZVQN.js} +4 -4
  92. package/dist/{omp-Z6FRNDPN.js → omp-2QMEY6HD.js} +138 -18
  93. package/dist/omp-2QMEY6HD.js.map +1 -0
  94. package/dist/{openclaw-7G437NT7.js → openclaw-UZC437RW.js} +4 -4
  95. package/dist/{opencode-Q6KBSAKW.js → opencode-L4BGFP25.js} +87 -27
  96. package/dist/opencode-L4BGFP25.js.map +1 -0
  97. package/dist/{package-RMPN57YZ.js → package-DNLQTPNN.js} +8 -7
  98. package/dist/{package-RMPN57YZ.js.map → package-DNLQTPNN.js.map} +1 -1
  99. package/dist/{pi-LZT6WWAZ.js → pi-PVVPVWKG.js} +3 -2
  100. package/dist/{pi-LZT6WWAZ.js.map → pi-PVVPVWKG.js.map} +1 -1
  101. package/dist/{qwen-code-WQBKRPWJ.js → qwen-code-G4CXKUHR.js} +426 -10
  102. package/dist/qwen-code-G4CXKUHR.js.map +1 -0
  103. package/dist/{roo-code-GWSNU3OW.js → roo-code-67TYJIQR.js} +3 -3
  104. package/dist/runtime/index.d.ts +1 -1
  105. package/dist/runtime/index.js +9 -8
  106. package/dist/sdk/index.d.ts +3 -3
  107. package/dist/sdk/index.js +4 -3
  108. package/dist/sdk/index.js.map +1 -1
  109. package/dist/sdk/test.d.ts +2 -2
  110. package/dist/sdk/test.js +6 -5
  111. package/dist/sdk/test.js.map +1 -1
  112. package/dist/{serve-D3GO36UT.js → serve-EOVWYBGZ.js} +11 -10
  113. package/dist/{serve-D3GO36UT.js.map → serve-EOVWYBGZ.js.map} +1 -1
  114. package/dist/{status-KOSKCETC.js → status-VTD6XXLW.js} +7 -6
  115. package/dist/{status-KOSKCETC.js.map → status-VTD6XXLW.js.map} +1 -1
  116. package/dist/{statusline-ZNTCL5YC.js → statusline-3ET5YPQ5.js} +10 -9
  117. package/dist/{statusline-ZNTCL5YC.js.map → statusline-3ET5YPQ5.js.map} +1 -1
  118. package/dist/{telemetry-XIKUUGTQ.js → telemetry-QCTMKDBQ.js} +6 -5
  119. package/dist/{telemetry-XIKUUGTQ.js.map → telemetry-QCTMKDBQ.js.map} +1 -1
  120. package/dist/{trae-NWT3TMSZ.js → trae-7CZZGOGI.js} +3 -3
  121. package/dist/{types-BEKsAium.d.ts → types-_6arHbX9.d.ts} +67 -24
  122. package/dist/{uninstall-2LBLQVFA.js → uninstall-FL3SH2DT.js} +11 -10
  123. package/dist/{uninstall-2LBLQVFA.js.map → uninstall-FL3SH2DT.js.map} +1 -1
  124. package/dist/{upgrade-ZC2S7VRN.js → upgrade-SLFG2FCT.js} +11 -10
  125. package/dist/{upgrade-ZC2S7VRN.js.map → upgrade-SLFG2FCT.js.map} +1 -1
  126. package/dist/{usage-E2A4O2CY.js → usage-IRBLVUZM.js} +3 -3
  127. package/dist/{usage-event-JKMC4QC4.js → usage-event-VFF77WYY.js} +10 -9
  128. package/dist/{usage-event-JKMC4QC4.js.map → usage-event-VFF77WYY.js.map} +1 -1
  129. package/dist/{vscode-copilot-ALV45XF6.js → vscode-copilot-MF2CDNMT.js} +23 -10
  130. package/dist/vscode-copilot-MF2CDNMT.js.map +1 -0
  131. package/dist/{warp-H6IX4U3J.js → warp-VKG55TAT.js} +55 -4
  132. package/dist/warp-VKG55TAT.js.map +1 -0
  133. package/dist/windsurf-FU5G4SHF.js +443 -0
  134. package/dist/windsurf-FU5G4SHF.js.map +1 -0
  135. package/dist/{zed-RH34TNSW.js → zed-SFUHL3GS.js} +3 -3
  136. package/package.json +2 -2
  137. package/dist/amp-B524U6LO.js +0 -300
  138. package/dist/amp-B524U6LO.js.map +0 -1
  139. package/dist/chunk-3PFFTFCU.js +0 -190
  140. package/dist/chunk-3PFFTFCU.js.map +0 -1
  141. package/dist/chunk-76FUSVIA.js.map +0 -1
  142. package/dist/chunk-ADTHT2FN.js.map +0 -1
  143. package/dist/chunk-IW77FFNX.js.map +0 -1
  144. package/dist/chunk-KGGBNKI4.js.map +0 -1
  145. package/dist/chunk-MAH6QQTO.js.map +0 -1
  146. package/dist/claude-code-MLWS7CW6.js.map +0 -1
  147. package/dist/codebuff-SH2S3QAN.js.map +0 -1
  148. package/dist/codex-BXA4FIF4.js.map +0 -1
  149. package/dist/copilot-cli-ROIBTTDT.js.map +0 -1
  150. package/dist/crush-QLEFLHUC.js.map +0 -1
  151. package/dist/cursor-ASJPWZ4R.js.map +0 -1
  152. package/dist/doctor-TJUGSQ5C.js.map +0 -1
  153. package/dist/droid-NCSS3B5Y.js.map +0 -1
  154. package/dist/gemini-cli-SLVWLXJW.js.map +0 -1
  155. package/dist/goose-PBA7KTE5.js.map +0 -1
  156. package/dist/hermes-ONDCECP2.js.map +0 -1
  157. package/dist/hook-OU522ABV.js.map +0 -1
  158. package/dist/jetbrains-copilot-YABZ3HFA.js.map +0 -1
  159. package/dist/kilo-F3ZDZKCT.js.map +0 -1
  160. package/dist/kilo-cli-3HWPWBY4.js.map +0 -1
  161. package/dist/kimi-NV72FVAO.js.map +0 -1
  162. package/dist/mimo-code-IMM5YM3M.js.map +0 -1
  163. package/dist/mux-DSSH4AFS.js.map +0 -1
  164. package/dist/omp-Z6FRNDPN.js.map +0 -1
  165. package/dist/opencode-Q6KBSAKW.js.map +0 -1
  166. package/dist/qwen-code-WQBKRPWJ.js.map +0 -1
  167. package/dist/vscode-copilot-ALV45XF6.js.map +0 -1
  168. package/dist/warp-H6IX4U3J.js.map +0 -1
  169. /package/dist/{antigravity-ANDKF3K7.js.map → antigravity-AMKKKHS4.js.map} +0 -0
  170. /package/dist/{antigravity-cli-LLRPDDOT.js.map → antigravity-cli-XERXEH5W.js.map} +0 -0
  171. /package/dist/{chunk-G3ER4SY5.js.map → chunk-5G4EPJ4Y.js.map} +0 -0
  172. /package/dist/{chunk-J3D5W5T2.js.map → chunk-EROJDXMW.js.map} +0 -0
  173. /package/dist/{chunk-22Y24V7O.js.map → chunk-F5RX62T7.js.map} +0 -0
  174. /package/dist/{chunk-LL4OBGRU.js.map → chunk-LQRAPXS4.js.map} +0 -0
  175. /package/dist/{chunk-KGJWE6A2.js.map → chunk-MZF2YCXX.js.map} +0 -0
  176. /package/dist/{chunk-CIRDOY2V.js.map → chunk-QOGMDOUX.js.map} +0 -0
  177. /package/dist/{chunk-6XHB6BE3.js.map → chunk-SCVBYSF7.js.map} +0 -0
  178. /package/dist/{kiro-C5W6AGDR.js.map → kiro-YL6AWUYG.js.map} +0 -0
  179. /package/dist/{leaderboard-TIBVONVT.js.map → leaderboard-V6VWA5ZV.js.map} +0 -0
  180. /package/dist/{nemoclaw-3EVNKD3I.js.map → nemoclaw-CQYXZVQN.js.map} +0 -0
  181. /package/dist/{openclaw-7G437NT7.js.map → openclaw-UZC437RW.js.map} +0 -0
  182. /package/dist/{roo-code-GWSNU3OW.js.map → roo-code-67TYJIQR.js.map} +0 -0
  183. /package/dist/{trae-NWT3TMSZ.js.map → trae-7CZZGOGI.js.map} +0 -0
  184. /package/dist/{usage-E2A4O2CY.js.map → usage-IRBLVUZM.js.map} +0 -0
  185. /package/dist/{zed-RH34TNSW.js.map → zed-SFUHL3GS.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/pi/index.ts"],"sourcesContent":["/**\n * adapters/pi — Pi platform adapter for agent-connector.\n *\n * Pi is a file-based-skills host with NO writable MCP config and a native\n * prompt-template (slash command) surface. It has no lifecycle hook system in\n * the json-stdio or ts-plugin sense. Its extensibility surfaces are:\n *\n * - server → single \"skip\" (Pi has no writable MCP config).\n * - hooks → single \"skip\" (no json-stdio / ts-plugin hook layer).\n * - commands → write prompt templates under <piPromptsDir>/<name>.md.\n * project scope → <projectDir>/.pi/prompts/<name>.md\n * user scope → ~/.pi/agent/prompts/<name>.md\n * - skills → write <piSkillsDir>/<name>/SKILL.md (+ declared resources).\n * project scope → <projectDir>/.pi/skills/<name>/SKILL.md\n * user scope → ~/.pi/agent/skills/<name>/SKILL.md\n * - subagents → unsupported (BaseAdapter skip/warn).\n *\n * Ground-truth refs (docs/research/kilo-pi-ground-truth.md):\n * - prompt templates: docs/prompt-templates.md → ~/.pi/agent/prompts/*.md\n * and .pi/prompts/*.md, invoked as /name.\n * - global skills: ~/.pi/agent/skills/<n>/SKILL.md (NOT ~/.pi/skills/).\n * - project skills: .pi/skills/<n>/SKILL.md (unchanged).\n * - allowed-tools: space-delimited (NOT comma-delimited).\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, InstallContext } from \"../spi.js\";\nimport type {\n ChangeRecord,\n CommandDef,\n DetectedPlatform,\n HealthCheck,\n HookParadigm,\n PlatformCapabilities,\n PlatformId,\n SkillDef,\n} from \"../../core/types.js\";\n\nconst HOST: PlatformId = \"pi\";\n\nexport class PiAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Pi\";\n /**\n * Pi has no MCP surface at all — the paradigm label \"mcp-only\" is backwards\n * for a host with NO writable MCP config. Pi is purely a content-surface host\n * (skills + prompt templates). Use \"mcp-only\" only because the HookParadigm\n * union has no \"no-mcp\" variant; MCP is explicitly unsupported below.\n */\n readonly paradigm: HookParadigm = \"mcp-only\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (user scope: ~/.pi/agent/AGENTS.md per base.ts AGENTS_MD_USER_PATHS).\n supportsMemory: true,\n // Pi has no json-stdio / ts-plugin lifecycle hook system.\n preToolUse: false,\n postToolUse: false,\n preCompact: false,\n sessionStart: false,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n canModifyArgs: false,\n canModifyOutput: false,\n canInjectSessionContext: false,\n // No writable MCP config.\n transports: [],\n // Content surfaces: prompt-template commands + skills.\n supportsCommands: true,\n supportsSkills: true,\n supportsSubagents: false,\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".pi\");\n const projDir = join(projectDir, \".pi\");\n\n const userInstalled = existsSync(userDir);\n const projInstalled = existsSync(projDir);\n const installed = userInstalled || projInstalled;\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projDir : userDir;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? `found Pi config (${scope}) at ${configPath}`\n : `no .pi config at ${userDir} or ${projDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n /**\n * Base config dir (used for server/hook doctor paths only; content surfaces\n * use dedicated helpers below so the scope split is explicit).\n */\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\")\n : join(homedir(), \".pi\");\n }\n\n /**\n * Pi has no writable MCP config. Returning the config dir keeps the generic\n * doctor/backup sensible while installServer always skips.\n */\n getServerConfigPath(ctx: InstallContext): string {\n return this.getConfigDir(ctx);\n }\n\n /** Pi has no hook file — point at the config dir for generic doctor/backup. */\n getHookConfigPath(ctx: InstallContext): string {\n return this.getConfigDir(ctx);\n }\n\n /**\n * Prompt-templates dir (slash commands):\n * project scope → <projectDir>/.pi/prompts/\n * user scope → ~/.pi/agent/prompts/\n * (ground truth: docs/prompt-templates.md)\n */\n private promptsDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\", \"prompts\")\n : join(homedir(), \".pi\", \"agent\", \"prompts\");\n }\n\n /**\n * Skills dir:\n * project scope → <projectDir>/.pi/skills/\n * user scope → ~/.pi/agent/skills/\n * (ground truth: ~/.pi/agent/skills/ is what Pi actually reads for global skills)\n */\n private skillsDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\", \"skills\")\n : join(homedir(), \".pi\", \"agent\", \"skills\");\n }\n\n /** Native skill dir: <skillsDir>/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.skillsDir(ctx), name);\n }\n\n // ── MCP server (unavailable — Pi has no writable MCP config) ─────────────\n\n installServer(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"Pi has no writable MCP config\",\n },\n ];\n }\n\n uninstallServer(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"Pi has no writable MCP config\",\n },\n ];\n }\n\n // ── Hooks (unavailable — Pi has no json-stdio/ts-plugin hook layer) ───────\n\n installHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Pi has no hook layer)\",\n },\n ];\n }\n\n uninstallHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Pi has no hook layer)\",\n },\n ];\n }\n\n // ── Content surface: commands (prompt templates) ─────────────────────────\n // Pi slash commands are prompt template files: <promptsDir>/<name>.md.\n // Idempotent (byte-identical → skip) via BaseAdapter.writeContentFile and\n // reversible via removeContentFile. Honors platforms[\"pi\"].commands === false.\n\n /** Native prompt-template file path: <promptsDir>/<name>.md. */\n private commandPath(ctx: InstallContext, name: string): string {\n return join(this.promptsDir(ctx), `${name}.md`);\n }\n\n override installCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.commands === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"commands disabled for pi\" }];\n }\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.writeContentFile(\n this.commandPath(ctx, cmd.name),\n this.renderCommand(cmd),\n ctx.dryRun,\n ),\n );\n }\n\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\n );\n }\n\n /**\n * Render a command as a Pi prompt template (md+frontmatter).\n * Pi's format mirrors the generic command render: description, optional model,\n * optional argument-hint. The body is the prompt template text.\n */\n private renderCommand(cmd: CommandDef): string {\n const frontmatter: Record<string, unknown> = {};\n if (cmd.description !== undefined) frontmatter.description = cmd.description;\n if (cmd.model !== undefined) frontmatter.model = cmd.model;\n if (cmd.argumentHint !== undefined) frontmatter[\"argument-hint\"] = cmd.argumentHint;\n if (cmd.extra) Object.assign(frontmatter, cmd.extra);\n return this.renderFrontmatterMd(frontmatter, cmd.prompt);\n }\n\n // ── Content surface: skills ──────────────────────────────────────────────\n // CONTENT-ONLY: pure native-file writer under <skillsDir>/<name>.\n // Idempotent (byte-identical → skip) via BaseAdapter.writeContentFile and\n // reversible via removeContentFile. Honors platforms[\"pi\"].skills === false.\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for pi\" }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue;\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md.\n *\n * Pi's allowed-tools field is SPACE-delimited (not comma-delimited).\n * Every other field follows the uniform SKILL.md convention.\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n // Pi reads allowed-tools as space-separated — NOT the \", \" used by other hosts.\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\" \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const checks: HealthCheck[] = [];\n\n // Command (prompt-template) checks.\n for (const cmd of ctx.connector.commands) {\n const p = this.commandPath(ctx, cmd.name);\n checks.push({\n name: `${this.name}: command ${cmd.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: p }\n : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n\n // Skill-surface checks.\n for (const skill of ctx.connector.skills) {\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\n checks.push({\n name: `${this.name}: skill ${skill.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: p }\n : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n return checks;\n }\n}\n\nexport const adapter = new PiAdapter();\nexport default adapter;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/adapters/pi/index.ts"],"sourcesContent":["/**\n * adapters/pi — Pi platform adapter for agent-connector.\n *\n * Pi is a file-based-skills host with NO writable MCP config and a native\n * prompt-template (slash command) surface. It has no lifecycle hook system in\n * the json-stdio or ts-plugin sense. Its extensibility surfaces are:\n *\n * - server → single \"skip\" (Pi has no writable MCP config).\n * - hooks → single \"skip\" (no json-stdio / ts-plugin hook layer).\n * - commands → write prompt templates under <piPromptsDir>/<name>.md.\n * project scope → <projectDir>/.pi/prompts/<name>.md\n * user scope → ~/.pi/agent/prompts/<name>.md\n * - skills → write <piSkillsDir>/<name>/SKILL.md (+ declared resources).\n * project scope → <projectDir>/.pi/skills/<name>/SKILL.md\n * user scope → ~/.pi/agent/skills/<name>/SKILL.md\n * - subagents → unsupported (BaseAdapter skip/warn).\n *\n * Ground-truth refs (docs/research/kilo-pi-ground-truth.md):\n * - prompt templates: docs/prompt-templates.md → ~/.pi/agent/prompts/*.md\n * and .pi/prompts/*.md, invoked as /name.\n * - global skills: ~/.pi/agent/skills/<n>/SKILL.md (NOT ~/.pi/skills/).\n * - project skills: .pi/skills/<n>/SKILL.md (unchanged).\n * - allowed-tools: space-delimited (NOT comma-delimited).\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, InstallContext } from \"../spi.js\";\nimport type {\n ChangeRecord,\n CommandDef,\n DetectedPlatform,\n HealthCheck,\n HookParadigm,\n PlatformCapabilities,\n PlatformId,\n SkillDef,\n} from \"../../core/types.js\";\n\nconst HOST: PlatformId = \"pi\";\n\nexport class PiAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Pi\";\n /**\n * Pi has no MCP surface at all — the paradigm label \"mcp-only\" is backwards\n * for a host with NO writable MCP config. Pi is purely a content-surface host\n * (skills + prompt templates). Use \"mcp-only\" only because the HookParadigm\n * union has no \"no-mcp\" variant; MCP is explicitly unsupported below.\n */\n readonly paradigm: HookParadigm = \"mcp-only\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (user scope: ~/.pi/agent/AGENTS.md per base.ts AGENTS_MD_USER_PATHS).\n supportsMemory: true,\n // Pi has no json-stdio / ts-plugin lifecycle hook system.\n preToolUse: false,\n postToolUse: false,\n preCompact: false,\n sessionStart: false,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n canModifyArgs: false,\n canModifyOutput: false,\n canInjectSessionContext: false,\n // No writable MCP config.\n transports: [],\n // Content surfaces: prompt-template commands + skills.\n supportsCommands: true,\n supportsSkills: true,\n supportsSubagents: false,\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".pi\");\n const projDir = join(projectDir, \".pi\");\n\n const userInstalled = existsSync(userDir);\n const projInstalled = existsSync(projDir);\n const installed = userInstalled || projInstalled;\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projDir : userDir;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? `found Pi config (${scope}) at ${configPath}`\n : `no .pi config at ${userDir} or ${projDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n /**\n * Base config dir (used for server/hook doctor paths only; content surfaces\n * use dedicated helpers below so the scope split is explicit).\n */\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\")\n : join(homedir(), \".pi\");\n }\n\n /**\n * Pi has no writable MCP config. Returning the config dir keeps the generic\n * doctor/backup sensible while installServer always skips.\n */\n getServerConfigPath(ctx: InstallContext): string {\n return this.getConfigDir(ctx);\n }\n\n /** Pi has no hook file — point at the config dir for generic doctor/backup. */\n getHookConfigPath(ctx: InstallContext): string {\n return this.getConfigDir(ctx);\n }\n\n /**\n * Prompt-templates dir (slash commands):\n * project scope → <projectDir>/.pi/prompts/\n * user scope → ~/.pi/agent/prompts/\n * (ground truth: docs/prompt-templates.md)\n */\n private promptsDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\", \"prompts\")\n : join(homedir(), \".pi\", \"agent\", \"prompts\");\n }\n\n /**\n * Skills dir:\n * project scope → <projectDir>/.pi/skills/\n * user scope → ~/.pi/agent/skills/\n * (ground truth: ~/.pi/agent/skills/ is what Pi actually reads for global skills)\n */\n private skillsDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".pi\", \"skills\")\n : join(homedir(), \".pi\", \"agent\", \"skills\");\n }\n\n /** Native skill dir: <skillsDir>/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.skillsDir(ctx), name);\n }\n\n // ── MCP server (unavailable — Pi has no writable MCP config) ─────────────\n\n installServer(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"Pi has no writable MCP config\",\n },\n ];\n }\n\n uninstallServer(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"Pi has no writable MCP config\",\n },\n ];\n }\n\n // ── Hooks (unavailable — Pi has no json-stdio/ts-plugin hook layer) ───────\n\n installHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Pi has no hook layer)\",\n },\n ];\n }\n\n uninstallHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Pi has no hook layer)\",\n },\n ];\n }\n\n // ── Content surface: commands (prompt templates) ─────────────────────────\n // Pi slash commands are prompt template files: <promptsDir>/<name>.md.\n // Idempotent (byte-identical → skip) via BaseAdapter.writeContentFile and\n // reversible via removeContentFile. Honors platforms[\"pi\"].commands === false.\n\n /** Native prompt-template file path: <promptsDir>/<name>.md. */\n private commandPath(ctx: InstallContext, name: string): string {\n return join(this.promptsDir(ctx), `${name}.md`);\n }\n\n override installCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.commands === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"commands disabled for pi\" }];\n }\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.writeContentFile(\n this.commandPath(ctx, cmd.name),\n this.renderCommand(cmd),\n ctx.dryRun,\n ),\n );\n }\n\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\n );\n }\n\n /**\n * Render a command as a Pi prompt template (md+frontmatter).\n * Pi's format mirrors the generic command render: description, optional model,\n * optional argument-hint. The body is the prompt template text.\n */\n private renderCommand(cmd: CommandDef): string {\n const frontmatter: Record<string, unknown> = {};\n if (cmd.description !== undefined) frontmatter.description = cmd.description;\n if (cmd.model !== undefined) frontmatter.model = cmd.model;\n if (cmd.argumentHint !== undefined) frontmatter[\"argument-hint\"] = cmd.argumentHint;\n if (cmd.extra) Object.assign(frontmatter, cmd.extra);\n return this.renderFrontmatterMd(frontmatter, cmd.prompt);\n }\n\n // ── Content surface: skills ──────────────────────────────────────────────\n // CONTENT-ONLY: pure native-file writer under <skillsDir>/<name>.\n // Idempotent (byte-identical → skip) via BaseAdapter.writeContentFile and\n // reversible via removeContentFile. Honors platforms[\"pi\"].skills === false.\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for pi\" }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue;\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md.\n *\n * Pi's allowed-tools field is SPACE-delimited (not comma-delimited).\n * Every other field follows the uniform SKILL.md convention.\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n // Pi reads allowed-tools as space-separated — NOT the \", \" used by other hosts.\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\" \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const checks: HealthCheck[] = [];\n\n // Command (prompt-template) checks.\n for (const cmd of ctx.connector.commands) {\n const p = this.commandPath(ctx, cmd.name);\n checks.push({\n name: `${this.name}: command ${cmd.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: p }\n : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n\n // Skill-surface checks.\n for (const skill of ctx.connector.skills) {\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\n checks.push({\n name: `${this.name}: skill ${skill.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: p }\n : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n return checks;\n }\n}\n\nexport const adapter = new PiAdapter();\nexport default adapter;\n"],"mappings":"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","names":[]}
@@ -1,4 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ addLedgerOwner,
4
+ createLedgerEntry,
5
+ describeJsonValue,
6
+ dropLedgerEntry,
7
+ findLedgerEntry,
8
+ hashJsonValue,
9
+ jsonDeepEquals,
10
+ ledgerEntriesOwnedBy,
11
+ loadConfigPatchLedger,
12
+ removeLedgerOwner,
13
+ saveConfigPatchLedger
14
+ } from "./chunk-FAHIMJRU.js";
2
15
  import {
3
16
  writeTomlString
4
17
  } from "./chunk-OPXAP3AC.js";
@@ -7,16 +20,18 @@ import {
7
20
  } from "./chunk-DBQMX75U.js";
8
21
  import {
9
22
  BaseAdapter
10
- } from "./chunk-IW77FFNX.js";
23
+ } from "./chunk-CPLK7GTE.js";
11
24
  import {
12
25
  renderSkillMd
13
26
  } from "./chunk-7L6ITC4T.js";
14
27
  import {
15
28
  buildHomeBinHookCommand,
29
+ buildHomeBinStatuslineCommand,
16
30
  buildServeWrapperCommand,
17
31
  isHomeBinHookCommand,
32
+ isHomeBinStatuslineCommand,
18
33
  shouldWrapForTelemetry
19
- } from "./chunk-BXGFUENQ.js";
34
+ } from "./chunk-I5MLE6S7.js";
20
35
  import "./chunk-N6HH7K6P.js";
21
36
  import "./chunk-TRQKHP7D.js";
22
37
 
@@ -26,6 +41,7 @@ import { homedir } from "os";
26
41
  import { join } from "path";
27
42
  var HOST = "qwen-code";
28
43
  var MCP_ROOT_KEY = "mcpServers";
44
+ var STATUSLINE_KEY = "ui.statusLine";
29
45
  var QwenCodeAdapter = class extends BaseAdapter {
30
46
  id = HOST;
31
47
  name = "Qwen CLI";
@@ -39,6 +55,10 @@ var QwenCodeAdapter = class extends BaseAdapter {
39
55
  preToolUse: true,
40
56
  postToolUse: true,
41
57
  preCompact: true,
58
+ // Qwen fires PostCompact natively (observe-only — "not in the decision mode
59
+ // supported events list"; QwenLM/qwen-code docs/users/features/hooks.md
60
+ // "#### PostCompact", trigger + compact_summary payload).
61
+ postCompact: true,
42
62
  sessionStart: true,
43
63
  sessionEnd: true,
44
64
  userPromptSubmit: true,
@@ -63,7 +83,22 @@ var QwenCodeAdapter = class extends BaseAdapter {
63
83
  // Ground truth: kilo-pi-ground-truth.md § "Already-known skills gaps".
64
84
  supportsCommands: true,
65
85
  supportsSkills: true,
66
- supportsSubagents: true
86
+ supportsSubagents: true,
87
+ // Statusline surface: qwen-code is the 2nd v1 host (after claude-code).
88
+ // installStatusline wires settings.json `ui.statusLine` = {type:"command",
89
+ // command:<home-bin statusline cmd>} through the SAME refcounted ownership
90
+ // ledger as configPatch (never clobbers a ui.statusLine agent-connector does
91
+ // not own). The key is NESTED here (`ui.statusLine`), unlike claude-code's
92
+ // top-level `statusLine`. Confirmed config key + stdin payload against the
93
+ // qwen-code status-line docs (shipped v0.14.3, PR #2923).
94
+ supportsStatusline: true,
95
+ // Native passthrough: Qwen's 16-event surface includes 3 host-specific events
96
+ // with NO canonical HookEventName analog — TodoCreated / TodoCompleted /
97
+ // StopFailure (QwenLM/qwen-code docs/users/features/hooks.md). A connector
98
+ // reaches them via platforms["qwen-code"].nativeHooks; installHooks files the
99
+ // PascalCase event key VERBATIM into settings.json hooks, and the generic
100
+ // uninstall reverses it.
101
+ supportsNativeHooks: true
67
102
  };
68
103
  // ── Detection ────────────────────────────────────────────────────────────
69
104
  detectInstalled(projectDir) {
@@ -198,18 +233,26 @@ var QwenCodeAdapter = class extends BaseAdapter {
198
233
  // ── Hook install / uninstall ─────────────────────────────────────────────
199
234
  installHooks(ctx) {
200
235
  const { connector } = ctx;
201
- if (connector.platforms[HOST]?.hooks === false) {
202
- return [{ platform: this.id, action: "skip", detail: "hooks disabled for qwen-code" }];
203
- }
204
- if (connector.hookEvents.length === 0) {
205
- return [{ platform: this.id, action: "skip", detail: "connector declares no hooks" }];
236
+ const override = connector.platforms[HOST];
237
+ const hooksDisabled = override?.hooks === false;
238
+ const normalizedEvents = hooksDisabled ? [] : connector.hookEvents;
239
+ const nativeHooks = override?.nativeHooks ?? {};
240
+ const nativeEvents = Object.keys(nativeHooks);
241
+ if (normalizedEvents.length === 0 && nativeEvents.length === 0) {
242
+ return [
243
+ {
244
+ platform: this.id,
245
+ action: "skip",
246
+ detail: hooksDisabled ? "hooks disabled for qwen-code" : "connector declares no hooks"
247
+ }
248
+ ];
206
249
  }
207
250
  const settingsPath = this.getHookConfigPath(ctx);
208
251
  const settings = this.readJson(settingsPath) ?? {};
209
252
  const hooks = settings.hooks ??= {};
210
253
  const changes = [];
211
254
  let mutated = false;
212
- for (const event of connector.hookEvents) {
255
+ for (const event of normalizedEvents) {
213
256
  const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);
214
257
  const matcher = connector.hooks[event]?.matcher ?? "";
215
258
  const entry = {
@@ -246,6 +289,32 @@ var QwenCodeAdapter = class extends BaseAdapter {
246
289
  }
247
290
  mutated = true;
248
291
  }
292
+ for (const nativeEvent of nativeEvents) {
293
+ const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, nativeEvent, connector.id);
294
+ const entry = {
295
+ matcher: nativeHooks[nativeEvent]?.matcher ?? "",
296
+ hooks: [{ type: "command", command }]
297
+ };
298
+ const bucket = hooks[nativeEvent] ??= [];
299
+ const existingIdx = bucket.findIndex((e) => e.hooks?.[0]?.command === command);
300
+ if (existingIdx >= 0) {
301
+ if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {
302
+ changes.push({
303
+ platform: this.id,
304
+ action: "skip",
305
+ path: settingsPath,
306
+ detail: `hooks.${nativeEvent} (native) already registered`
307
+ });
308
+ continue;
309
+ }
310
+ bucket[existingIdx] = entry;
311
+ changes.push({ platform: this.id, action: "update", path: settingsPath, detail: `hooks.${nativeEvent} (native)` });
312
+ } else {
313
+ bucket.push(entry);
314
+ changes.push({ platform: this.id, action: "create", path: settingsPath, detail: `hooks.${nativeEvent} (native)` });
315
+ }
316
+ mutated = true;
317
+ }
249
318
  if (mutated) this.writeJson(settingsPath, settings, ctx.dryRun);
250
319
  return changes;
251
320
  }
@@ -307,6 +376,282 @@ var QwenCodeAdapter = class extends BaseAdapter {
307
376
  isOurCommand(command, ctx) {
308
377
  return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);
309
378
  }
379
+ // ── Statusline surface (a HUD/status line) ────────────────────────────────
380
+ // Wires settings.json `ui.statusLine` = { type:"command", command:<home-bin
381
+ // statusline cmd> } through the SAME refcounted ownership ledger as configPatch
382
+ // (the ledger primitives are surface-agnostic). qwen-code does NOT advertise
383
+ // supportsConfigPatch, so — unlike claude-code, which routes the statusline
384
+ // through its private applyConfigPatches loop — this adapter performs the nested
385
+ // set-if-absent write directly with the exported ledger primitives. Semantics
386
+ // are identical: never clobber a ui.statusLine agent-connector does not own
387
+ // (skip-warn), record prior state + owner (refcounted across connectors),
388
+ // reversible by uninstallStatusline (last-owner-verified delete). The key is
389
+ // NESTED (`ui.statusLine`), so we own the `ui.statusLine` leaf — if the user
390
+ // already has it, we skip-warn and never touch their value.
391
+ //
392
+ // The home-bin command makes Qwen exec
393
+ // `<homeBin> statusline qwen-code --connector <id>` for every status refresh,
394
+ // which re-imports the connector module and renders the line (runtime/
395
+ // statusline-entrypoint). No telemetry in v1.
396
+ /** The statusLine config object value agent-connector writes at `ui.statusLine`. */
397
+ statuslineValue(ctx) {
398
+ const command = buildHomeBinStatuslineCommand(ctx.homeBinPath, HOST, ctx.connector.id);
399
+ return { type: "command", command };
400
+ }
401
+ installStatusline(ctx) {
402
+ const { connector } = ctx;
403
+ if (connector.statusline == null) {
404
+ return [{ platform: this.id, action: "skip", detail: "connector declares no statusline" }];
405
+ }
406
+ if (connector.platforms[HOST]?.statusline === false) {
407
+ return [{ platform: this.id, action: "skip", detail: "statusline disabled for qwen-code" }];
408
+ }
409
+ const filePath = this.getServerConfigPath(ctx);
410
+ if (this.isPresentButUnparseable(filePath)) {
411
+ return [
412
+ {
413
+ platform: this.id,
414
+ action: "warn",
415
+ path: filePath,
416
+ detail: `existing ${filePath} is not parseable; statusline left unapplied (back it up / fix it, then re-run)`
417
+ }
418
+ ];
419
+ }
420
+ const settings = this.readJson(filePath) ?? {};
421
+ if (typeof settings !== "object" || Array.isArray(settings)) {
422
+ return [
423
+ {
424
+ platform: this.id,
425
+ action: "warn",
426
+ path: filePath,
427
+ detail: `existing ${filePath} is not a JSON object; statusline left unapplied`
428
+ }
429
+ ];
430
+ }
431
+ const ledger = loadConfigPatchLedger(ctx.dataRoot);
432
+ const desired = this.statuslineValue(ctx);
433
+ const segments = STATUSLINE_KEY.split(".");
434
+ const leaf = readJsonLeaf(settings, segments);
435
+ const entry = findLedgerEntry(ledger, HOST, filePath, STATUSLINE_KEY);
436
+ const changes = [];
437
+ if (leaf.kind === "blocked") {
438
+ return [
439
+ {
440
+ platform: this.id,
441
+ action: "warn",
442
+ path: filePath,
443
+ detail: `statusline ${STATUSLINE_KEY} skipped: "${leaf.atPath}" exists but is not an object \u2014 set ${STATUSLINE_KEY} manually if wanted`
444
+ }
445
+ ];
446
+ }
447
+ if (leaf.kind === "absent") {
448
+ writeJsonLeaf(settings, segments, desired);
449
+ this.writeJson(filePath, settings, ctx.dryRun);
450
+ if (entry) {
451
+ entry.writtenValue = desired;
452
+ entry.writtenValueHash = hashJsonValue(desired);
453
+ addLedgerOwner(entry, connector.id, connector.version);
454
+ } else {
455
+ createLedgerEntry(ledger, {
456
+ platform: HOST,
457
+ file: filePath,
458
+ key: STATUSLINE_KEY,
459
+ value: desired,
460
+ connectorId: connector.id,
461
+ connectorVersion: connector.version
462
+ });
463
+ }
464
+ if (!ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);
465
+ changes.push({
466
+ platform: this.id,
467
+ action: "create",
468
+ path: filePath,
469
+ detail: `statusline ${STATUSLINE_KEY}: <absent> \u2192 ${describeJsonValue(desired)}`
470
+ });
471
+ return changes;
472
+ }
473
+ if (!entry) {
474
+ return [
475
+ {
476
+ platform: this.id,
477
+ action: "warn",
478
+ path: filePath,
479
+ detail: `statusline ${STATUSLINE_KEY} skipped: already set to ${describeJsonValue(leaf.value)} (not created by agent-connector) \u2014 left untouched`
480
+ }
481
+ ];
482
+ }
483
+ if (!jsonDeepEquals(leaf.value, entry.writtenValue)) {
484
+ return [
485
+ {
486
+ platform: this.id,
487
+ action: "warn",
488
+ path: filePath,
489
+ detail: `statusline ${STATUSLINE_KEY}: value changed since install (current ${describeJsonValue(leaf.value)}, wrote ${describeJsonValue(entry.writtenValue)}); leaving in place`
490
+ }
491
+ ];
492
+ }
493
+ if (jsonDeepEquals(desired, leaf.value)) {
494
+ const owners = entry.owners.map((o) => o.connectorId);
495
+ if (addLedgerOwner(entry, connector.id, connector.version)) {
496
+ if (!ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);
497
+ return [
498
+ {
499
+ platform: this.id,
500
+ action: "skip",
501
+ path: filePath,
502
+ detail: `statusline ${STATUSLINE_KEY} already installed; registered as co-owner (co-owned with ${owners.join(", ")})`
503
+ }
504
+ ];
505
+ }
506
+ return [
507
+ {
508
+ platform: this.id,
509
+ action: "skip",
510
+ path: filePath,
511
+ detail: `statusline ${STATUSLINE_KEY} already installed`
512
+ }
513
+ ];
514
+ }
515
+ return [
516
+ {
517
+ platform: this.id,
518
+ action: "warn",
519
+ path: filePath,
520
+ detail: `statusline ${STATUSLINE_KEY} skipped: already owned by ${entry.owners.map((o) => o.connectorId).join(", ")} with a different value \u2014 left untouched`
521
+ }
522
+ ];
523
+ }
524
+ uninstallStatusline(ctx) {
525
+ const ledger = loadConfigPatchLedger(ctx.dataRoot);
526
+ const owned = ledgerEntriesOwnedBy(ledger, HOST, ctx.connector.id).filter(
527
+ (e) => e.key === STATUSLINE_KEY
528
+ );
529
+ if (owned.length === 0) {
530
+ return [
531
+ {
532
+ platform: this.id,
533
+ action: "skip",
534
+ detail: "statusline: no ownership recorded; left untouched"
535
+ }
536
+ ];
537
+ }
538
+ const changes = [];
539
+ let ledgerMutated = false;
540
+ const byFile = /* @__PURE__ */ new Map();
541
+ for (const entry of owned) {
542
+ const bucket = byFile.get(entry.file) ?? [];
543
+ bucket.push(entry);
544
+ byFile.set(entry.file, bucket);
545
+ }
546
+ for (const [filePath, entries] of byFile) {
547
+ const unparseable = this.isPresentButUnparseable(filePath);
548
+ const settings = unparseable ? null : this.readJson(filePath);
549
+ let fileMutated = false;
550
+ for (const entry of entries) {
551
+ const { lastOwner } = removeLedgerOwner(entry, ctx.connector.id);
552
+ ledgerMutated = true;
553
+ if (!lastOwner) {
554
+ changes.push({
555
+ platform: this.id,
556
+ action: "skip",
557
+ path: filePath,
558
+ detail: `statusline ${entry.key} retained: still owned by ${entry.owners.map((o) => o.connectorId).join(", ")}`
559
+ });
560
+ continue;
561
+ }
562
+ dropLedgerEntry(ledger, entry);
563
+ if (unparseable) {
564
+ changes.push({
565
+ platform: this.id,
566
+ action: "warn",
567
+ path: filePath,
568
+ detail: `statusline ${entry.key}: ${filePath} is not parseable; key left in place (ownership released)`
569
+ });
570
+ continue;
571
+ }
572
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
573
+ changes.push({
574
+ platform: this.id,
575
+ action: "skip",
576
+ path: filePath,
577
+ detail: `statusline ${entry.key} already absent (no settings file); ownership record dropped`
578
+ });
579
+ continue;
580
+ }
581
+ const leaf = readJsonLeaf(settings, entry.key.split("."));
582
+ if (leaf.kind !== "present") {
583
+ changes.push({
584
+ platform: this.id,
585
+ action: "skip",
586
+ path: filePath,
587
+ detail: `statusline ${entry.key} already absent; ownership record dropped`
588
+ });
589
+ continue;
590
+ }
591
+ if (entry.prior?.present !== false || !jsonDeepEquals(leaf.value, entry.writtenValue)) {
592
+ changes.push({
593
+ platform: this.id,
594
+ action: "warn",
595
+ path: filePath,
596
+ detail: `statusline ${entry.key}: value changed since install (current ${describeJsonValue(leaf.value)}, wrote ${describeJsonValue(entry.writtenValue)}); left in place`
597
+ });
598
+ continue;
599
+ }
600
+ deleteJsonLeaf(settings, entry.key.split("."));
601
+ fileMutated = true;
602
+ changes.push({
603
+ platform: this.id,
604
+ action: "remove",
605
+ path: filePath,
606
+ detail: `statusline ${entry.key} removed (was ${describeJsonValue(entry.writtenValue)})`
607
+ });
608
+ }
609
+ if (fileMutated && settings) this.writeJson(filePath, settings, ctx.dryRun);
610
+ }
611
+ if (ledgerMutated && !ctx.dryRun) saveConfigPatchLedger(ctx.dataRoot, ledger);
612
+ return changes;
613
+ }
614
+ /**
615
+ * Parse Qwen's statusLine stdin JSON into the normalized
616
+ * {@link StatuslineContext}. Qwen pipes a JSON object to the statusLine command
617
+ * on stdin (model, context_window, workspace, git, …); fields the payload omits
618
+ * stay undefined. `raw` keeps the verbatim payload (incl. version/git/metrics/
619
+ * vim). NOTE: Qwen has NO cost analog (no cost field in the payload), so
620
+ * `ctx.cost` is intentionally left undefined. Sources: qwen-code status-line
621
+ * docs, QwenLM/qwen-code settings.md.
622
+ */
623
+ parseStatusInput(raw) {
624
+ const input = raw ?? {};
625
+ const ctx = {
626
+ host: HOST,
627
+ capabilities: this.capabilities,
628
+ raw
629
+ };
630
+ if (typeof input.session_id === "string" && input.session_id !== "") {
631
+ ctx.sessionId = input.session_id;
632
+ }
633
+ if (typeof input.workspace?.current_dir === "string") {
634
+ ctx.cwd = input.workspace.current_dir;
635
+ }
636
+ if (typeof input.model?.display_name === "string") {
637
+ ctx.model = { displayName: input.model.display_name };
638
+ }
639
+ const cw = input.context_window;
640
+ if (cw) {
641
+ const context = {};
642
+ if (typeof cw.context_window_size === "number") context.maxTokens = cw.context_window_size;
643
+ if (typeof cw.current_usage === "number") context.usedTokens = cw.current_usage;
644
+ if (typeof cw.used_percentage === "number") context.percent = cw.used_percentage;
645
+ if (context.usedTokens !== void 0 || context.maxTokens !== void 0 || context.percent !== void 0) {
646
+ ctx.context = context;
647
+ }
648
+ }
649
+ return ctx;
650
+ }
651
+ /** Format the rendered status line into Qwen's native reply: stdout = line, exit 0. */
652
+ formatStatusOutput(rendered) {
653
+ return { exitCode: 0, stdout: rendered };
654
+ }
310
655
  // ── Content surfaces: commands / subagents ───────────────────────────────
311
656
  // CONTENT-ONLY: pure native-file writers under <qwenDir>/{commands,agents}. No
312
657
  // runtime dispatch, no home-bin pointer, no telemetry wrap. Each method is
@@ -513,6 +858,32 @@ var QwenCodeAdapter = class extends BaseAdapter {
513
858
  check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
514
859
  });
515
860
  }
861
+ const statuslineLedgerOwned = ledgerEntriesOwnedBy(
862
+ loadConfigPatchLedger(ctx.dataRoot),
863
+ HOST,
864
+ connectorId
865
+ ).some((e) => e.key === STATUSLINE_KEY);
866
+ if ((ctx.connector.statusline != null || statuslineLedgerOwned) && ctx.connector.platforms[HOST]?.statusline !== false) {
867
+ checks.push({
868
+ name: `${this.name}: statusline wired`,
869
+ check: () => {
870
+ const settings = this.readJson(
871
+ settingsPath
872
+ );
873
+ const command = settings?.ui?.statusLine?.command;
874
+ if (command === void 0) {
875
+ return { status: "FAIL", detail: `ui.statusLine not set in ${settingsPath}` };
876
+ }
877
+ if (typeof command === "string" && isHomeBinStatuslineCommand(command, homeBin, connectorId)) {
878
+ return { status: "OK", detail: "ui.statusLine command present" };
879
+ }
880
+ return {
881
+ status: "FAIL",
882
+ detail: `ui.statusLine in ${settingsPath} is not agent-connector's (left untouched)`
883
+ };
884
+ }
885
+ });
886
+ }
516
887
  return checks;
517
888
  }
518
889
  // ── Runtime: parse Qwen stdin JSON → normalized event ────────────────────
@@ -574,6 +945,13 @@ var QwenCodeAdapter = class extends BaseAdapter {
574
945
  };
575
946
  return ev;
576
947
  }
948
+ case "PostCompact": {
949
+ const ev = {
950
+ ...base,
951
+ ...input.trigger === "auto" || input.trigger === "manual" ? { trigger: input.trigger } : {}
952
+ };
953
+ return ev;
954
+ }
577
955
  case "Stop": {
578
956
  const ev = {
579
957
  ...base,
@@ -713,6 +1091,44 @@ var QwenCodeAdapter = class extends BaseAdapter {
713
1091
  return { exitCode: 0, stdout: JSON.stringify(payload) };
714
1092
  }
715
1093
  };
1094
+ function readJsonLeaf(root, segments) {
1095
+ let node = root;
1096
+ for (let i = 0; i < segments.length - 1; i++) {
1097
+ const next = node[segments[i]];
1098
+ if (next === void 0) return { kind: "absent" };
1099
+ if (next === null || typeof next !== "object" || Array.isArray(next)) {
1100
+ return { kind: "blocked", atPath: segments.slice(0, i + 1).join(".") };
1101
+ }
1102
+ node = next;
1103
+ }
1104
+ const leaf = node[segments[segments.length - 1]];
1105
+ if (leaf === void 0) return { kind: "absent" };
1106
+ return { kind: "present", value: leaf };
1107
+ }
1108
+ function writeJsonLeaf(root, segments, value) {
1109
+ let node = root;
1110
+ for (let i = 0; i < segments.length - 1; i++) {
1111
+ const seg = segments[i];
1112
+ const next = node[seg];
1113
+ if (next === void 0) {
1114
+ const created = {};
1115
+ node[seg] = created;
1116
+ node = created;
1117
+ } else {
1118
+ node = next;
1119
+ }
1120
+ }
1121
+ node[segments[segments.length - 1]] = value;
1122
+ }
1123
+ function deleteJsonLeaf(root, segments) {
1124
+ let node = root;
1125
+ for (let i = 0; i < segments.length - 1; i++) {
1126
+ const next = node[segments[i]];
1127
+ if (next === null || typeof next !== "object" || Array.isArray(next)) return;
1128
+ node = next;
1129
+ }
1130
+ delete node[segments[segments.length - 1]];
1131
+ }
716
1132
  function extractSessionId(input) {
717
1133
  if (typeof input.session_id === "string" && input.session_id !== "") {
718
1134
  return input.session_id;
@@ -751,4 +1167,4 @@ export {
751
1167
  adapter,
752
1168
  qwen_code_default as default
753
1169
  };
754
- //# sourceMappingURL=qwen-code-WQBKRPWJ.js.map
1170
+ //# sourceMappingURL=qwen-code-G4CXKUHR.js.map