agentsys 5.0.3 → 5.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 (264) hide show
  1. package/.claude-plugin/marketplace.json +21 -14
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/AGENTS.md +2 -1
  4. package/CHANGELOG.md +18 -0
  5. package/README.md +7 -6
  6. package/adapters/codex/skills/agnix/SKILL.md +0 -1
  7. package/adapters/codex/skills/audit-project/SKILL.md +0 -1
  8. package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
  9. package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
  10. package/adapters/codex/skills/consult/SKILL.md +132 -57
  11. package/adapters/codex/skills/debate/SKILL.md +214 -0
  12. package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
  13. package/adapters/codex/skills/deslop/SKILL.md +0 -1
  14. package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
  15. package/adapters/codex/skills/enhance/SKILL.md +0 -1
  16. package/adapters/codex/skills/learn/SKILL.md +0 -1
  17. package/adapters/codex/skills/next-task/SKILL.md +0 -1
  18. package/adapters/codex/skills/perf/SKILL.md +0 -1
  19. package/adapters/codex/skills/repo-map/SKILL.md +0 -1
  20. package/adapters/codex/skills/ship/SKILL.md +0 -1
  21. package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
  22. package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
  23. package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
  24. package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
  25. package/adapters/opencode/agents/agent-enhancer.md +0 -1
  26. package/adapters/opencode/agents/agnix-agent.md +0 -1
  27. package/adapters/opencode/agents/ci-fixer.md +0 -1
  28. package/adapters/opencode/agents/ci-monitor.md +0 -1
  29. package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
  30. package/adapters/opencode/agents/consult-agent.md +122 -30
  31. package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
  32. package/adapters/opencode/agents/debate-orchestrator.md +169 -0
  33. package/adapters/opencode/agents/delivery-validator.md +0 -1
  34. package/adapters/opencode/agents/deslop-agent.md +0 -1
  35. package/adapters/opencode/agents/docs-enhancer.md +0 -1
  36. package/adapters/opencode/agents/exploration-agent.md +0 -1
  37. package/adapters/opencode/agents/hooks-enhancer.md +0 -1
  38. package/adapters/opencode/agents/implementation-agent.md +0 -1
  39. package/adapters/opencode/agents/learn-agent.md +0 -1
  40. package/adapters/opencode/agents/map-validator.md +0 -1
  41. package/adapters/opencode/agents/perf-analyzer.md +0 -1
  42. package/adapters/opencode/agents/perf-code-paths.md +0 -1
  43. package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
  44. package/adapters/opencode/agents/perf-orchestrator.md +0 -1
  45. package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
  46. package/adapters/opencode/agents/perf-theory-tester.md +0 -1
  47. package/adapters/opencode/agents/plan-synthesizer.md +0 -1
  48. package/adapters/opencode/agents/planning-agent.md +0 -1
  49. package/adapters/opencode/agents/plugin-enhancer.md +0 -1
  50. package/adapters/opencode/agents/prompt-enhancer.md +0 -1
  51. package/adapters/opencode/agents/simple-fixer.md +0 -1
  52. package/adapters/opencode/agents/skills-enhancer.md +0 -1
  53. package/adapters/opencode/agents/sync-docs-agent.md +0 -1
  54. package/adapters/opencode/agents/task-discoverer.md +0 -1
  55. package/adapters/opencode/agents/test-coverage-checker.md +0 -1
  56. package/adapters/opencode/agents/worktree-manager.md +0 -1
  57. package/adapters/opencode/commands/agnix.md +0 -1
  58. package/adapters/opencode/commands/audit-project-agents.md +0 -1
  59. package/adapters/opencode/commands/audit-project-github.md +0 -1
  60. package/adapters/opencode/commands/audit-project.md +0 -1
  61. package/adapters/opencode/commands/consult.md +133 -57
  62. package/adapters/opencode/commands/debate.md +224 -0
  63. package/adapters/opencode/commands/delivery-approval.md +0 -1
  64. package/adapters/opencode/commands/deslop.md +0 -1
  65. package/adapters/opencode/commands/drift-detect.md +0 -1
  66. package/adapters/opencode/commands/enhance.md +0 -1
  67. package/adapters/opencode/commands/learn.md +0 -1
  68. package/adapters/opencode/commands/next-task.md +0 -1
  69. package/adapters/opencode/commands/perf.md +0 -1
  70. package/adapters/opencode/commands/repo-map.md +0 -1
  71. package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
  72. package/adapters/opencode/commands/ship-deployment.md +0 -1
  73. package/adapters/opencode/commands/ship-error-handling.md +0 -1
  74. package/adapters/opencode/commands/ship.md +0 -1
  75. package/adapters/opencode/commands/sync-docs.md +0 -1
  76. package/adapters/opencode/skills/agnix/SKILL.md +1 -2
  77. package/adapters/opencode/skills/consult/SKILL.md +33 -23
  78. package/adapters/opencode/skills/debate/SKILL.md +245 -0
  79. package/adapters/opencode/skills/deslop/SKILL.md +1 -2
  80. package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
  81. package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
  82. package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
  83. package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
  84. package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
  85. package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
  86. package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
  87. package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
  88. package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
  89. package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
  90. package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
  91. package/adapters/opencode/skills/learn/SKILL.md +1 -2
  92. package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
  93. package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
  94. package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
  95. package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
  96. package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
  97. package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
  98. package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
  99. package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
  100. package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
  101. package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
  102. package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
  103. package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
  104. package/lib/adapter-transforms.js +24 -4
  105. package/package.json +1 -1
  106. package/plugins/agnix/.claude-plugin/plugin.json +1 -1
  107. package/plugins/agnix/skills/agnix/SKILL.md +1 -1
  108. package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
  109. package/plugins/audit-project/lib/adapter-transforms.js +24 -4
  110. package/plugins/consult/.claude-plugin/plugin.json +1 -1
  111. package/plugins/consult/agents/consult-agent.md +122 -29
  112. package/plugins/consult/commands/consult.md +135 -58
  113. package/plugins/consult/skills/consult/SKILL.md +31 -20
  114. package/plugins/debate/.claude-plugin/plugin.json +21 -0
  115. package/plugins/debate/agents/debate-orchestrator.md +175 -0
  116. package/plugins/debate/commands/debate.md +221 -0
  117. package/plugins/debate/lib/adapter-transforms.js +298 -0
  118. package/plugins/debate/lib/collectors/codebase.js +392 -0
  119. package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
  120. package/plugins/debate/lib/collectors/documentation.js +219 -0
  121. package/plugins/debate/lib/collectors/github.js +330 -0
  122. package/plugins/debate/lib/collectors/index.js +126 -0
  123. package/plugins/debate/lib/config/index.js +14 -0
  124. package/plugins/debate/lib/cross-platform/index.js +539 -0
  125. package/plugins/debate/lib/discovery/index.js +352 -0
  126. package/plugins/debate/lib/drift-detect/collectors.js +37 -0
  127. package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
  128. package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
  129. package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
  130. package/plugins/debate/lib/enhance/benchmark.js +417 -0
  131. package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
  132. package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
  133. package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
  134. package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
  135. package/plugins/debate/lib/enhance/fixer.js +721 -0
  136. package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
  137. package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
  138. package/plugins/debate/lib/enhance/index.js +127 -0
  139. package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
  140. package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
  141. package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
  142. package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
  143. package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
  144. package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
  145. package/plugins/debate/lib/enhance/reporter.js +1348 -0
  146. package/plugins/debate/lib/enhance/security-patterns.js +284 -0
  147. package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
  148. package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
  149. package/plugins/debate/lib/enhance/suppression.js +352 -0
  150. package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
  151. package/plugins/debate/lib/index.js +270 -0
  152. package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
  153. package/plugins/debate/lib/patterns/pipeline.js +948 -0
  154. package/plugins/debate/lib/patterns/review-patterns.js +558 -0
  155. package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
  156. package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
  157. package/plugins/debate/lib/perf/analyzer/index.js +22 -0
  158. package/plugins/debate/lib/perf/argument-parser.js +105 -0
  159. package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
  160. package/plugins/debate/lib/perf/baseline-store.js +127 -0
  161. package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
  162. package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
  163. package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
  164. package/plugins/debate/lib/perf/checkpoint.js +123 -0
  165. package/plugins/debate/lib/perf/code-paths.js +86 -0
  166. package/plugins/debate/lib/perf/consolidation.js +37 -0
  167. package/plugins/debate/lib/perf/constraint-runner.js +71 -0
  168. package/plugins/debate/lib/perf/experiment-runner.js +32 -0
  169. package/plugins/debate/lib/perf/index.js +41 -0
  170. package/plugins/debate/lib/perf/investigation-state.js +874 -0
  171. package/plugins/debate/lib/perf/optimization-runner.js +79 -0
  172. package/plugins/debate/lib/perf/profilers/go.js +22 -0
  173. package/plugins/debate/lib/perf/profilers/index.js +46 -0
  174. package/plugins/debate/lib/perf/profilers/java.js +23 -0
  175. package/plugins/debate/lib/perf/profilers/node.js +27 -0
  176. package/plugins/debate/lib/perf/profilers/python.js +23 -0
  177. package/plugins/debate/lib/perf/profilers/rust.js +23 -0
  178. package/plugins/debate/lib/perf/profiling-runner.js +75 -0
  179. package/plugins/debate/lib/perf/schemas.js +140 -0
  180. package/plugins/debate/lib/platform/detect-platform.js +413 -0
  181. package/plugins/debate/lib/platform/detection-configs.js +93 -0
  182. package/plugins/debate/lib/platform/state-dir.js +132 -0
  183. package/plugins/debate/lib/platform/verify-tools.js +182 -0
  184. package/plugins/debate/lib/repo-map/cache.js +152 -0
  185. package/plugins/debate/lib/repo-map/concurrency.js +29 -0
  186. package/plugins/debate/lib/repo-map/index.js +222 -0
  187. package/plugins/debate/lib/repo-map/installer.js +212 -0
  188. package/plugins/debate/lib/repo-map/queries/go.js +27 -0
  189. package/plugins/debate/lib/repo-map/queries/index.js +100 -0
  190. package/plugins/debate/lib/repo-map/queries/java.js +38 -0
  191. package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
  192. package/plugins/debate/lib/repo-map/queries/python.js +24 -0
  193. package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
  194. package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
  195. package/plugins/debate/lib/repo-map/runner.js +1364 -0
  196. package/plugins/debate/lib/repo-map/updater.js +562 -0
  197. package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
  198. package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
  199. package/plugins/debate/lib/schemas/validator.js +247 -0
  200. package/plugins/debate/lib/sources/custom-handler.js +199 -0
  201. package/plugins/debate/lib/sources/policy-questions.js +246 -0
  202. package/plugins/debate/lib/sources/source-cache.js +165 -0
  203. package/plugins/debate/lib/state/workflow-state.js +576 -0
  204. package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
  205. package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
  206. package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
  207. package/plugins/debate/lib/types/index.d.ts +84 -0
  208. package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
  209. package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
  210. package/plugins/debate/lib/utils/atomic-write.js +94 -0
  211. package/plugins/debate/lib/utils/cache-manager.js +159 -0
  212. package/plugins/debate/lib/utils/command-parser.js +0 -0
  213. package/plugins/debate/lib/utils/context-optimizer.js +300 -0
  214. package/plugins/debate/lib/utils/deprecation.js +37 -0
  215. package/plugins/debate/lib/utils/shell-escape.js +88 -0
  216. package/plugins/debate/lib/utils/state-helpers.js +61 -0
  217. package/plugins/debate/skills/debate/SKILL.md +264 -0
  218. package/plugins/deslop/.claude-plugin/plugin.json +1 -1
  219. package/plugins/deslop/lib/adapter-transforms.js +24 -4
  220. package/plugins/deslop/skills/deslop/SKILL.md +1 -1
  221. package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
  222. package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
  223. package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
  224. package/plugins/enhance/.claude-plugin/plugin.json +1 -1
  225. package/plugins/enhance/lib/adapter-transforms.js +24 -4
  226. package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
  227. package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
  228. package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
  229. package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
  230. package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
  231. package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
  232. package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
  233. package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
  234. package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
  235. package/plugins/learn/.claude-plugin/plugin.json +1 -1
  236. package/plugins/learn/agents/learn-agent.md +1 -1
  237. package/plugins/learn/lib/adapter-transforms.js +24 -4
  238. package/plugins/learn/skills/learn/SKILL.md +1 -1
  239. package/plugins/next-task/.claude-plugin/plugin.json +1 -1
  240. package/plugins/next-task/agents/exploration-agent.md +1 -1
  241. package/plugins/next-task/lib/adapter-transforms.js +24 -4
  242. package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
  243. package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
  244. package/plugins/perf/.claude-plugin/plugin.json +1 -1
  245. package/plugins/perf/lib/adapter-transforms.js +24 -4
  246. package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
  247. package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
  248. package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
  249. package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
  250. package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
  251. package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
  252. package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
  253. package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
  254. package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
  255. package/plugins/repo-map/lib/adapter-transforms.js +24 -4
  256. package/plugins/ship/.claude-plugin/plugin.json +1 -1
  257. package/plugins/ship/lib/adapter-transforms.js +24 -4
  258. package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
  259. package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
  260. package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
  261. package/scripts/gen-adapters.js +6 -7
  262. package/scripts/generate-docs.js +4 -2
  263. package/scripts/plugins.txt +1 -0
  264. package/site/content.json +6 -6
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Custom Source Handler
3
+ * Handles follow-up questions and tool probing for custom sources
4
+ *
5
+ * @module lib/sources/custom-handler
6
+ */
7
+
8
+ const { execFileSync } = require('child_process');
9
+ const sourceCache = require('./source-cache');
10
+
11
+ /**
12
+ * Validate tool name to prevent command injection
13
+ * Only allows alphanumeric, hyphens, and underscores
14
+ * @param {string} toolName - Tool name to validate
15
+ * @returns {boolean} True if valid
16
+ */
17
+ function isValidToolName(toolName) {
18
+ return /^[a-zA-Z0-9_-]+$/.test(toolName);
19
+ }
20
+
21
+ /**
22
+ * Source types for custom selection
23
+ */
24
+ const SOURCE_TYPES = {
25
+ MCP: 'mcp',
26
+ CLI: 'cli',
27
+ SKILL: 'skill',
28
+ FILE: 'file'
29
+ };
30
+
31
+ /**
32
+ * Build follow-up questions for custom source
33
+ * Returns AskUserQuestion-compatible structure
34
+ * @returns {Object} Questions object for AskUserQuestion tool
35
+ */
36
+ function getCustomTypeQuestion() {
37
+ return {
38
+ header: 'Source Type',
39
+ question: 'What type of source is this?',
40
+ options: [
41
+ { label: 'CLI Tool', description: 'Command-line tool (e.g., tea, glab, jira-cli)' },
42
+ { label: 'MCP Server', description: 'Model Context Protocol server' },
43
+ { label: 'Skill/Plugin', description: 'Claude Code skill or plugin' },
44
+ { label: 'File Path', description: 'Local file with tasks (markdown, JSON, etc.)' }
45
+ ],
46
+ multiSelect: false
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Build name/path question based on type
52
+ * @param {string} type - Source type (mcp, cli, skill, file)
53
+ * @returns {Object} Questions object for AskUserQuestion tool
54
+ */
55
+ function getCustomNameQuestion(type) {
56
+ const prompts = {
57
+ cli: { header: 'CLI Tool', question: 'What is the CLI tool name?', hint: 'e.g., tea, glab, jira' },
58
+ mcp: { header: 'MCP Server', question: 'What is the MCP server name?', hint: 'e.g., gitea-mcp, linear-mcp' },
59
+ skill: { header: 'Skill Name', question: 'What is the skill name?', hint: 'e.g., linear:list-issues' },
60
+ file: { header: 'File Path', question: 'What is the file path?', hint: 'e.g., ./backlog.md, /docs/tasks.json' }
61
+ };
62
+ return prompts[type] || prompts.cli;
63
+ }
64
+
65
+ /**
66
+ * Map user's type selection to internal type
67
+ * @param {string} selection - User's selection label
68
+ * @returns {string} Internal type (mcp, cli, skill, file)
69
+ */
70
+ function mapTypeSelection(selection) {
71
+ const map = {
72
+ 'CLI Tool': SOURCE_TYPES.CLI,
73
+ 'MCP Server': SOURCE_TYPES.MCP,
74
+ 'Skill/Plugin': SOURCE_TYPES.SKILL,
75
+ 'File Path': SOURCE_TYPES.FILE
76
+ };
77
+ return map[selection] || SOURCE_TYPES.CLI;
78
+ }
79
+
80
+ /**
81
+ * Probe CLI tool for available commands
82
+ * @param {string} toolName - CLI tool name
83
+ * @returns {Object} Discovered capabilities
84
+ */
85
+ function probeCLI(toolName) {
86
+ const capabilities = {
87
+ type: 'cli',
88
+ tool: toolName,
89
+ available: false,
90
+ features: [],
91
+ commands: {}
92
+ };
93
+
94
+ // Validate tool name to prevent command injection
95
+ if (!isValidToolName(toolName)) {
96
+ console.error(`Invalid tool name: ${toolName}`);
97
+ return capabilities;
98
+ }
99
+
100
+ try {
101
+ // Check if tool exists using execFileSync (prevents command injection)
102
+ execFileSync(toolName, ['--version'], { encoding: 'utf8', stdio: 'pipe' });
103
+ capabilities.available = true;
104
+ } catch {
105
+ return capabilities;
106
+ }
107
+
108
+ // Known CLI patterns
109
+ const knownPatterns = {
110
+ tea: {
111
+ features: ['issues', 'prs', 'reviews'],
112
+ commands: {
113
+ list_issues: 'tea issues list',
114
+ get_issue: 'tea issues view {id}',
115
+ create_pr: 'tea pulls create --title {title} --base {base} --head {head}',
116
+ list_prs: 'tea pulls list',
117
+ get_pr: 'tea pr {id}'
118
+ }
119
+ },
120
+ glab: {
121
+ features: ['issues', 'prs', 'ci'],
122
+ commands: {
123
+ list_issues: 'glab issue list',
124
+ get_issue: 'glab issue view {id}',
125
+ create_pr: 'glab mr create --title {title} --target-branch {base} --source-branch {head}',
126
+ list_prs: 'glab mr list',
127
+ get_pr: 'glab mr view {id}',
128
+ ci_status: 'glab ci status'
129
+ }
130
+ },
131
+ gh: {
132
+ features: ['issues', 'prs', 'ci'],
133
+ commands: {
134
+ list_issues: 'gh issue list',
135
+ get_issue: 'gh issue view {id}',
136
+ create_pr: 'gh pr create --title {title} --base {base} --head {head}',
137
+ list_prs: 'gh pr list',
138
+ get_pr: 'gh pr view {id}',
139
+ ci_status: 'gh pr checks {id}'
140
+ }
141
+ }
142
+ };
143
+
144
+ // Use known pattern if available
145
+ if (knownPatterns[toolName]) {
146
+ capabilities.features = knownPatterns[toolName].features;
147
+ capabilities.commands = knownPatterns[toolName].commands;
148
+ capabilities.pattern = 'known';
149
+ } else {
150
+ // Unknown tool - try to discover via help
151
+ capabilities.pattern = 'discovered';
152
+ capabilities.features = ['unknown'];
153
+ capabilities.commands = {
154
+ help: `${toolName} --help`
155
+ };
156
+ }
157
+
158
+ return capabilities;
159
+ }
160
+
161
+ /**
162
+ * Build complete custom source config and cache it
163
+ * @param {string} type - Source type
164
+ * @param {string} name - Tool name or path
165
+ * @returns {Object} Complete source configuration
166
+ */
167
+ function buildCustomConfig(type, name) {
168
+ const config = {
169
+ source: 'custom',
170
+ type: type,
171
+ tool: name
172
+ };
173
+
174
+ // Probe capabilities for CLI tools
175
+ if (type === SOURCE_TYPES.CLI) {
176
+ const capabilities = probeCLI(name);
177
+ config.capabilities = capabilities;
178
+
179
+ // Cache capabilities for fast access
180
+ if (capabilities.available) {
181
+ sourceCache.saveToolCapabilities(name, capabilities);
182
+ }
183
+ }
184
+
185
+ // Save preference
186
+ sourceCache.savePreference(config);
187
+
188
+ return config;
189
+ }
190
+
191
+ module.exports = {
192
+ SOURCE_TYPES,
193
+ getCustomTypeQuestion,
194
+ getCustomNameQuestion,
195
+ mapTypeSelection,
196
+ probeCLI,
197
+ buildCustomConfig,
198
+ isValidToolName
199
+ };
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Policy Questions Builder
3
+ * Builds AskUserQuestion-ready structure with cache awareness
4
+ *
5
+ * @module lib/sources/policy-questions
6
+ */
7
+
8
+ const sourceCache = require('./source-cache');
9
+ const customHandler = require('./custom-handler');
10
+
11
+ /**
12
+ * Source label mapping for proper casing
13
+ */
14
+ const SOURCE_LABELS = {
15
+ github: 'GitHub',
16
+ gitlab: 'GitLab',
17
+ local: 'Local',
18
+ custom: 'Custom',
19
+ other: 'Other'
20
+ };
21
+
22
+ /**
23
+ * Get policy questions with cache-aware options
24
+ * Call this once - returns full question structure ready for AskUserQuestion
25
+ *
26
+ * @returns {Object} { questions: [...], cachedPreference: {...}|null }
27
+ */
28
+ function getPolicyQuestions() {
29
+ const cached = sourceCache.getPreference();
30
+
31
+ // Build source options
32
+ const sourceOptions = [];
33
+
34
+ // If cached, add as first option
35
+ // NOTE: OpenCode enforces 30-char max on labels
36
+ if (cached) {
37
+ const cachedLabel = cached.source === 'custom'
38
+ ? `${cached.tool} (${cached.type})`
39
+ : SOURCE_LABELS[cached.source] || (cached.source.charAt(0).toUpperCase() + cached.source.slice(1));
40
+
41
+ // Truncate to fit within 30 chars: "X (last used)" where X can be max 17 chars
42
+ const maxBaseLen = 30 - ' (last used)'.length; // 18 chars for base
43
+ const truncatedLabel = cachedLabel.length > maxBaseLen
44
+ ? cachedLabel.substring(0, maxBaseLen - 1) + '…'
45
+ : cachedLabel;
46
+
47
+ sourceOptions.push({
48
+ label: `${truncatedLabel} (last used)`,
49
+ description: `Use your previous choice: ${cachedLabel}`
50
+ });
51
+ }
52
+
53
+ // Standard options
54
+ sourceOptions.push(
55
+ { label: 'GitHub Issues', description: 'Use gh CLI to list issues' },
56
+ { label: 'GitLab Issues', description: 'Use glab CLI to list issues' },
57
+ { label: 'Local tasks.md', description: 'Read from PLAN.md, tasks.md, or TODO.md' },
58
+ { label: 'Custom', description: 'Specify your tool: CLI, MCP, Skill, or file path' },
59
+ { label: 'Other', description: 'Describe your source - agent figures it out' }
60
+ );
61
+
62
+ return {
63
+ questions: [
64
+ {
65
+ header: 'Source',
66
+ question: 'Where should I look for tasks?',
67
+ options: sourceOptions,
68
+ multiSelect: false
69
+ },
70
+ {
71
+ header: 'Priority',
72
+ question: 'What type of tasks to prioritize?',
73
+ options: [
74
+ { label: 'All', description: 'Consider all tasks, pick by score' },
75
+ { label: 'Bugs', description: 'Focus on bug fixes' },
76
+ { label: 'Security', description: 'Security issues first' },
77
+ { label: 'Features', description: 'New feature development' }
78
+ ],
79
+ multiSelect: false
80
+ },
81
+ {
82
+ header: 'Stop Point',
83
+ question: 'How far should I take this task?',
84
+ options: [
85
+ { label: 'Merged', description: 'Until PR is merged to main' },
86
+ { label: 'PR Created', description: 'Stop after creating PR' },
87
+ { label: 'Implemented', description: 'Stop after local implementation' },
88
+ { label: 'Deployed', description: 'Deploy to staging' },
89
+ { label: 'Production', description: 'Full production deployment' }
90
+ ],
91
+ multiSelect: false
92
+ }
93
+ ],
94
+ cachedPreference: cached
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Get custom source follow-up questions
100
+ * Call after user selects "Custom"
101
+ *
102
+ * @returns {Object} Question structure for custom type selection
103
+ */
104
+ function getCustomTypeQuestions() {
105
+ return {
106
+ questions: [customHandler.getCustomTypeQuestion()]
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Get custom name question based on type
112
+ * @param {string} type - cli, mcp, skill, or file
113
+ * @returns {Object} Question structure for tool/path name
114
+ */
115
+ function getCustomNameQuestion(type) {
116
+ const q = customHandler.getCustomNameQuestion(type);
117
+ return {
118
+ questions: [{
119
+ header: q.header,
120
+ question: q.question,
121
+ options: [], // Free text input via "Other"
122
+ multiSelect: false
123
+ }]
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Parse policy responses and build policy object
129
+ * Also handles caching
130
+ *
131
+ * @param {Object} responses - User's answers
132
+ * @param {string} responses.source - Source selection
133
+ * @param {string} responses.priority - Priority selection
134
+ * @param {string} responses.stopPoint - Stop point selection
135
+ * @param {Object} [responses.custom] - Custom source details (if applicable)
136
+ * @returns {Object} Policy object ready for workflow state
137
+ */
138
+ function parseAndCachePolicy(responses) {
139
+ const policy = {
140
+ taskSource: mapSource(responses.source, responses.custom),
141
+ priorityFilter: mapPriority(responses.priority),
142
+ stoppingPoint: mapStopPoint(responses.stopPoint)
143
+ };
144
+
145
+ // Cache source preference (unless "other" which is ad-hoc)
146
+ if (policy.taskSource.source !== 'other') {
147
+ sourceCache.savePreference(policy.taskSource);
148
+ }
149
+
150
+ return policy;
151
+ }
152
+
153
+ /**
154
+ * Map source selection to policy value
155
+ */
156
+ function mapSource(selection, customDetails) {
157
+ // Check if user selected cached option
158
+ if (selection.includes('(last used)')) {
159
+ return sourceCache.getPreference();
160
+ }
161
+
162
+ const sourceMap = {
163
+ 'GitHub Issues': { source: 'github' },
164
+ 'GitLab Issues': { source: 'gitlab' },
165
+ 'Local tasks.md': { source: 'local' },
166
+ 'Custom': null, // Handled separately
167
+ 'Other': null // Handled separately
168
+ };
169
+
170
+ if (selection === 'Custom' && customDetails) {
171
+ // Normalize type label to internal value (e.g., "CLI Tool" -> "cli")
172
+ const normalizedType = customHandler.mapTypeSelection(customDetails.type);
173
+ const config = customHandler.buildCustomConfig(normalizedType, customDetails.name);
174
+ return config;
175
+ }
176
+
177
+ if (selection === 'Other') {
178
+ return { source: 'other', description: customDetails?.description || '' };
179
+ }
180
+
181
+ return sourceMap[selection] || { source: 'github' };
182
+ }
183
+
184
+ /**
185
+ * Map priority selection to policy value
186
+ */
187
+ function mapPriority(selection) {
188
+ const map = {
189
+ 'All': 'all',
190
+ 'Bugs': 'bugs',
191
+ 'Security': 'security',
192
+ 'Features': 'features'
193
+ };
194
+ return map[selection] || 'all';
195
+ }
196
+
197
+ /**
198
+ * Map stop point selection to policy value
199
+ */
200
+ function mapStopPoint(selection) {
201
+ const map = {
202
+ 'Merged': 'merged',
203
+ 'PR Created': 'pr-created',
204
+ 'Implemented': 'implemented',
205
+ 'Deployed': 'deployed',
206
+ 'Production': 'production'
207
+ };
208
+ return map[selection] || 'merged';
209
+ }
210
+
211
+ /**
212
+ * Check if user selected cached preference
213
+ * @param {string} selection - User's source selection
214
+ * @returns {boolean}
215
+ */
216
+ function isUsingCached(selection) {
217
+ return selection.includes('(last used)');
218
+ }
219
+
220
+ /**
221
+ * Check if custom follow-up is needed
222
+ * @param {string} selection - User's source selection
223
+ * @returns {boolean}
224
+ */
225
+ function needsCustomFollowUp(selection) {
226
+ return selection === 'Custom';
227
+ }
228
+
229
+ /**
230
+ * Check if "other" description is needed
231
+ * @param {string} selection - User's source selection
232
+ * @returns {boolean}
233
+ */
234
+ function needsOtherDescription(selection) {
235
+ return selection === 'Other';
236
+ }
237
+
238
+ module.exports = {
239
+ getPolicyQuestions,
240
+ getCustomTypeQuestions,
241
+ getCustomNameQuestion,
242
+ parseAndCachePolicy,
243
+ isUsingCached,
244
+ needsCustomFollowUp,
245
+ needsOtherDescription
246
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Source Cache
3
+ * File-based persistence for task source preferences
4
+ *
5
+ * State directory is platform-aware:
6
+ * - Claude Code: .claude/sources/
7
+ * - OpenCode: .opencode/sources/
8
+ * - Codex CLI: .codex/sources/
9
+ *
10
+ * @module lib/sources/source-cache
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { getStateDir } = require('../platform/state-dir');
16
+ const { writeJsonAtomic } = require('../utils/atomic-write');
17
+
18
+ const PREFERENCE_FILE = 'preference.json';
19
+
20
+ /**
21
+ * Get the sources directory path (platform-aware)
22
+ * @returns {string} Path to sources directory
23
+ */
24
+ function getSourcesDir() {
25
+ return path.join(getStateDir(), 'sources');
26
+ }
27
+
28
+ /**
29
+ * Validate tool name to prevent path traversal
30
+ * @param {string} toolName - Tool name to validate
31
+ * @returns {boolean} True if valid
32
+ */
33
+ function isValidToolName(toolName) {
34
+ // Prevent path traversal and shell metacharacters
35
+ return /^[a-zA-Z0-9_-]+$/.test(toolName);
36
+ }
37
+
38
+ /**
39
+ * Ensure sources directory exists
40
+ * @returns {string} Path to sources directory
41
+ */
42
+ function ensureDir() {
43
+ const sourcesDir = getSourcesDir();
44
+ if (!fs.existsSync(sourcesDir)) {
45
+ fs.mkdirSync(sourcesDir, { recursive: true });
46
+ }
47
+ return sourcesDir;
48
+ }
49
+
50
+ /**
51
+ * Get cached source preference
52
+ * @returns {Object|null} Preference object or null if not cached
53
+ * @example
54
+ * // Returns: { source: 'github' }
55
+ * // Or: { source: 'custom', type: 'cli', tool: 'tea' }
56
+ */
57
+ function getPreference() {
58
+ const filePath = path.join(getSourcesDir(), PREFERENCE_FILE);
59
+ if (!fs.existsSync(filePath)) {
60
+ return null;
61
+ }
62
+ try {
63
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
64
+ } catch (err) {
65
+ console.error(`Failed to read preference file:`, err.message);
66
+ return null;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Save source preference
72
+ * @param {Object} preference - Preference object
73
+ * @param {string} preference.source - Source type (github, gitlab, local, custom, other)
74
+ * @param {string} [preference.type] - For custom: mcp, cli, skill, file
75
+ * @param {string} [preference.tool] - Tool name or path
76
+ * @param {string} [preference.description] - For other: user's free text
77
+ */
78
+ function savePreference(preference) {
79
+ ensureDir();
80
+ const filePath = path.join(getSourcesDir(), PREFERENCE_FILE);
81
+ writeJsonAtomic(filePath, {
82
+ ...preference,
83
+ savedAt: new Date().toISOString()
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Get cached tool capabilities (for custom sources)
89
+ * @param {string} toolName - Tool identifier (e.g., 'tea', 'glab')
90
+ * @returns {Object|null} Capabilities object or null
91
+ */
92
+ function getToolCapabilities(toolName) {
93
+ // Prevent path traversal
94
+ if (!isValidToolName(toolName)) {
95
+ console.error(`Invalid tool name: ${toolName}`);
96
+ return null;
97
+ }
98
+ const filePath = path.join(getSourcesDir(), `${toolName}.json`);
99
+ if (!fs.existsSync(filePath)) {
100
+ return null;
101
+ }
102
+ try {
103
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
104
+ } catch (err) {
105
+ console.error(`Failed to read tool capabilities for ${toolName}:`, err.message);
106
+ return null;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Save tool capabilities after discovery
112
+ * @param {string} toolName - Tool identifier
113
+ * @param {Object} capabilities - Discovered capabilities
114
+ * @param {string[]} capabilities.features - Available features (issues, prs, ci)
115
+ * @param {Object} capabilities.commands - Command mappings
116
+ */
117
+ function saveToolCapabilities(toolName, capabilities) {
118
+ // Prevent path traversal
119
+ if (!isValidToolName(toolName)) {
120
+ console.error(`Invalid tool name: ${toolName}`);
121
+ return;
122
+ }
123
+ ensureDir();
124
+ const filePath = path.join(getSourcesDir(), `${toolName}.json`);
125
+ fs.writeFileSync(filePath, JSON.stringify({
126
+ ...capabilities,
127
+ discoveredAt: new Date().toISOString()
128
+ }, null, 2));
129
+ }
130
+
131
+ /**
132
+ * Clear all cached preferences
133
+ */
134
+ function clearCache() {
135
+ const sourcesDir = getSourcesDir();
136
+ if (fs.existsSync(sourcesDir)) {
137
+ const files = fs.readdirSync(sourcesDir);
138
+ for (const file of files) {
139
+ const filePath = path.join(sourcesDir, file);
140
+ const stats = fs.statSync(filePath);
141
+ if (stats.isFile()) {
142
+ fs.unlinkSync(filePath);
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Check if preference matches a specific source
150
+ * @param {string} source - Source to check
151
+ * @returns {boolean} True if preference matches
152
+ */
153
+ function isPreferred(source) {
154
+ const pref = getPreference();
155
+ return pref?.source === source;
156
+ }
157
+
158
+ module.exports = {
159
+ getPreference,
160
+ savePreference,
161
+ getToolCapabilities,
162
+ saveToolCapabilities,
163
+ clearCache,
164
+ isPreferred
165
+ };