@paulp-o/opencode-auq 1.6.0 → 2.0.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.
package/README.md CHANGED
@@ -5,8 +5,8 @@ OpenCode plugin that forwards `ask_user_questions` to the `auq ask` CLI.
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install -g auq-mcp-server
9
- npm install -g @paulp-o/opencode-auq
8
+ bun add -g auq-mcp-server
9
+ bun add -g @paulp-o/opencode-auq
10
10
  ```
11
11
 
12
12
  ## Configure OpenCode
@@ -4,7 +4,7 @@
4
4
  * This file is generated from src/shared/schemas.ts by scripts/sync-plugin-schemas.mjs
5
5
  * Run "npm run sync-plugin-schemas" to regenerate.
6
6
  *
7
- * Generated at: 2026-02-01T12:12:16.659Z
7
+ * Generated at: 2026-02-06T03:21:22.056Z
8
8
  */
9
9
  /**
10
10
  * Comprehensive tool description - single source of truth.
@@ -1 +1 @@
1
- {"version":3,"file":"generated-schemas.d.ts","sourceRoot":"","sources":["../src/generated-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoEH;;;GAGG;AACH,QAAA,MAAM,gBAAgB,QAUwD,CAAC;AAI/E,eAAO,MAAM,oBAAoB,EAAE,GAA4C,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"generated-schemas.d.ts","sourceRoot":"","sources":["../src/generated-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA4IH;;;GAGG;AACH,QAAA,MAAM,gBAAgB,QAWoD,CAAC;AAI3E,eAAO,MAAM,oBAAoB,EAAE,GAA4C,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -4,10 +4,29 @@
4
4
  * This file is generated from src/shared/schemas.ts by scripts/sync-plugin-schemas.mjs
5
5
  * Run "npm run sync-plugin-schemas" to regenerate.
6
6
  *
7
- * Generated at: 2026-02-01T12:12:16.659Z
7
+ * Generated at: 2026-02-06T03:21:22.056Z
8
8
  */
9
9
  import { tool } from "@opencode-ai/plugin/tool";
10
10
  const z = tool.schema;
11
+ /**
12
+ * Default limits - can be overridden by config
13
+ * These are the hard maximums enforced by the schema (config can only reduce, not increase)
14
+ */
15
+ const SCHEMA_LIMITS = {
16
+ MAX_OPTIONS: 10,
17
+ MAX_QUESTIONS: 10,
18
+ MIN_OPTIONS: 2,
19
+ MIN_QUESTIONS: 1,
20
+ };
21
+ /**
22
+ * Default recommended limits (used when no config is provided)
23
+ */
24
+ const DEFAULT_LIMITS = {
25
+ maxOptions: 5,
26
+ maxQuestions: 5,
27
+ recommendedOptions: 4,
28
+ recommendedQuestions: 4,
29
+ };
11
30
  const OptionSchema = z.object({
12
31
  label: z
13
32
  .string()
@@ -19,36 +38,68 @@ const OptionSchema = z.object({
19
38
  .describe("Explanation of what this option means or what will happen if chosen. " +
20
39
  "Useful for providing context about trade-offs or implications."),
21
40
  });
22
- const QuestionSchema = z.object({
23
- prompt: z
24
- .string()
25
- .describe("The complete question to ask the user. Should be clear, specific, and end with a question mark. " +
26
- "Example: 'Which programming language do you want to use?' " +
27
- "If multiSelect is true, phrase it accordingly, e.g. 'Which features do you want to enable?'"),
28
- title: z
29
- .string()
30
- .min(1, "Question title is required. Provide a short summary like 'Language' or 'Framework'.")
31
- .describe("Very short label displayed as a chip/tag (max 12 chars). " +
32
- "Examples: 'Auth method', 'Library', 'Approach'. " +
33
- "This title appears in the interface to help users quickly identify questions."),
34
- options: z
35
- .array(OptionSchema)
36
- .min(2)
37
- .max(4)
38
- .describe("The available choices for this question. Must have 2-4 options. " +
39
- "Each option should be a distinct, mutually exclusive choice (unless multiSelect is enabled). " +
40
- "There should be no 'Other' option, that will be provided automatically."),
41
- multiSelect: z
42
- .boolean()
43
- .describe("Set to true to allow the user to select multiple options instead of just one. " +
44
- "Use when choices are not mutually exclusive. Default: false (single-select)"),
45
- });
46
- const QuestionsSchema = z.array(QuestionSchema).min(1).max(4);
47
- const AskUserQuestionsParametersSchema = z.object({
48
- questions: QuestionsSchema.describe("Questions to ask the user (1-4 questions). " +
49
- "Each question must include: prompt (full question text), title (short label, max 12 chars), " +
50
- "options (2-4 choices with labels and descriptions), and multiSelect (boolean)."),
51
- });
41
+ /**
42
+ * Create a QuestionSchema with configurable option limits
43
+ * @param maxOptions - Maximum number of options allowed (default: 4, max: 10)
44
+ */
45
+ function createQuestionSchema(maxOptions = DEFAULT_LIMITS.maxOptions) {
46
+ // Clamp to valid range
47
+ const effectiveMax = Math.min(Math.max(maxOptions, SCHEMA_LIMITS.MIN_OPTIONS), SCHEMA_LIMITS.MAX_OPTIONS);
48
+ return z.object({
49
+ prompt: z
50
+ .string()
51
+ .describe("The complete question to ask the user. Should be clear, specific, and end with a question mark. " +
52
+ "Example: 'Which programming language do you want to use?' " +
53
+ "If multiSelect is true, phrase it accordingly, e.g. 'Which features do you want to enable?'"),
54
+ title: z
55
+ .string()
56
+ .min(1, "Question title is required. Provide a short summary like 'Language' or 'Framework'.")
57
+ .describe("Very short label displayed as a chip/tag (max 12 chars). " +
58
+ "Examples: 'Auth method', 'Library', 'Approach'. " +
59
+ "This title appears in the interface to help users quickly identify questions."),
60
+ options: z
61
+ .array(OptionSchema)
62
+ .min(SCHEMA_LIMITS.MIN_OPTIONS)
63
+ .max(effectiveMax)
64
+ .describe(`The available choices for this question. Must have ${SCHEMA_LIMITS.MIN_OPTIONS}-${effectiveMax} options. ` +
65
+ "Each option should be a distinct, mutually exclusive choice (unless multiSelect is enabled). " +
66
+ "There should be no 'Other' option, that will be provided automatically."),
67
+ multiSelect: z
68
+ .boolean()
69
+ .describe("Set to true to allow the user to select multiple options instead of just one. " +
70
+ "Use when choices are not mutually exclusive. Default: false (single-select)"),
71
+ });
72
+ }
73
+ /**
74
+ * Create a QuestionsSchema with configurable limits
75
+ * @param maxQuestions - Maximum number of questions allowed (default: 4, max: 10)
76
+ * @param maxOptions - Maximum number of options per question (default: 4, max: 10)
77
+ */
78
+ function createQuestionsSchema(maxQuestions = DEFAULT_LIMITS.maxQuestions, maxOptions = DEFAULT_LIMITS.maxOptions) {
79
+ const effectiveMaxQuestions = Math.min(Math.max(maxQuestions, SCHEMA_LIMITS.MIN_QUESTIONS), SCHEMA_LIMITS.MAX_QUESTIONS);
80
+ const questionSchema = createQuestionSchema(maxOptions);
81
+ return z
82
+ .array(questionSchema)
83
+ .min(SCHEMA_LIMITS.MIN_QUESTIONS)
84
+ .max(effectiveMaxQuestions);
85
+ }
86
+ /**
87
+ * Create the full parameters schema with configurable limits
88
+ * @param maxQuestions - Maximum number of questions (default: 4, max: 10)
89
+ * @param maxOptions - Maximum number of options per question (default: 4, max: 10)
90
+ */
91
+ function createAskUserQuestionsParametersSchema(maxQuestions = DEFAULT_LIMITS.maxQuestions, maxOptions = DEFAULT_LIMITS.maxOptions) {
92
+ const questionsSchema = createQuestionsSchema(maxQuestions, maxOptions);
93
+ return z.object({
94
+ questions: questionsSchema.describe(`Questions to ask the user (1-${maxQuestions} questions). ` +
95
+ `Each question must include: prompt (full question text), title (short label, max 12 chars), ` +
96
+ `options (2-${maxOptions} choices with labels and descriptions), and multiSelect (boolean).`),
97
+ });
98
+ }
99
+ // Default schemas for backward compatibility (using DEFAULT_LIMITS)
100
+ const QuestionSchema = createQuestionSchema();
101
+ const QuestionsSchema = createQuestionsSchema();
102
+ const AskUserQuestionsParametersSchema = createAskUserQuestionsParametersSchema();
52
103
  /**
53
104
  * Comprehensive tool description - single source of truth.
54
105
  * Used by MCP server and should be synced to opencode-plugin.
@@ -59,10 +110,11 @@ const TOOL_DESCRIPTION = "Use this tool when you need to ask the user questions
59
110
  "Get decisions on implementation choices as you work\n" +
60
111
  "Offer choices to the user about what direction to take.\n" +
61
112
  "Usage notes:\n\n" +
62
- "Users will always be able to select \"Other\" to provide custom text input\n" +
113
+ 'Users will always be able to select "Other" to provide custom text input\n' +
63
114
  "Use multiSelect: true to allow multiple answers to be selected for a question\n" +
64
- "Recommend an option unless absolutely necessary, make it the first option in the list and add \"(Recommended)\" at the end of the label\n" +
65
- "Do NOT use this tool to ask \"Is my plan ready?\" or \"Should I proceed?\"";
115
+ 'Recommend an option unless absolutely necessary, make it the first option in the list and add "(Recommended)" at the end of the label\n' +
116
+ 'For multiSelect questions, you MAY mark multiple options as "(Recommended)" if several choices are advisable\n' +
117
+ 'Do NOT use this tool to ask "Is my plan ready?" or "Should I proceed?"';
66
118
  // Only export what the plugin needs
67
119
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
120
  export const AskUserQuestionsArgs = AskUserQuestionsParametersSchema.shape;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAmDlD,eAAO,MAAM,sBAAsB,EAAE,MAcnC,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuFlD,eAAO,MAAM,sBAAsB,EAAE,MAgDnC,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,33 @@ import { tool } from "@opencode-ai/plugin/tool";
3
3
  // Schemas are auto-generated from src/shared/schemas.ts
4
4
  // Run "npm run sync-plugin-schemas" to regenerate
5
5
  import { AskUserQuestionsArgs, TOOL_DESCRIPTION } from "./generated-schemas.js";
6
+ // Map tool callID -> derived title (used by tool.execute.after)
7
+ const titleByCallId = new Map();
8
+ /**
9
+ * Generate a summary title for the question set
10
+ * Format: "Questions: [Q0] Language, [Q1] Framework"
11
+ */
12
+ function generateQuestionSummary(questions) {
13
+ const questionLabels = questions
14
+ .map((q, i) => `[Q${i}] ${q.title}`)
15
+ .join(", ");
16
+ return `Questions: ${questionLabels}`;
17
+ }
18
+ function safeSummaryFromArgs(args) {
19
+ if (!args || typeof args !== "object")
20
+ return null;
21
+ const maybeQuestions = args.questions;
22
+ if (!Array.isArray(maybeQuestions))
23
+ return null;
24
+ const questions = maybeQuestions;
25
+ const labels = questions
26
+ .map((q, i) => {
27
+ const title = (q.title || q.prompt || "").toString().trim();
28
+ return `[Q${i}] ${title || "(untitled)"}`;
29
+ })
30
+ .join(", ");
31
+ return `Questions: ${labels}`;
32
+ }
6
33
  const runAuqAsk = async (payload) => new Promise((resolve, reject) => {
7
34
  const child = spawn("auq", ["ask"], {
8
35
  stdio: ["pipe", "pipe", "pipe"],
@@ -41,9 +68,25 @@ export const AskUserQuestionsPlugin = async () => ({
41
68
  ask_user_questions: tool({
42
69
  description: TOOL_DESCRIPTION,
43
70
  args: AskUserQuestionsArgs,
44
- async execute(args) {
71
+ async execute(args, context) {
72
+ // Best-effort: set call metadata (some OpenCode UIs won't render this title).
73
+ const questions = args.questions;
74
+ const summary = generateQuestionSummary(questions);
75
+ context.metadata({
76
+ title: summary,
77
+ metadata: { auqQuestionSummary: summary },
78
+ });
45
79
  try {
46
- return await runAuqAsk({ questions: args.questions });
80
+ // Get working directory from OpenCode context
81
+ const workingDirectory = context.directory || context.worktree;
82
+ // Build payload explicitly
83
+ const payload = {
84
+ questions,
85
+ };
86
+ if (workingDirectory) {
87
+ payload.workingDirectory = workingDirectory;
88
+ }
89
+ return await runAuqAsk(payload);
47
90
  }
48
91
  catch (error) {
49
92
  return `Error in session: ${String(error)}`;
@@ -51,5 +94,22 @@ export const AskUserQuestionsPlugin = async () => ({
51
94
  },
52
95
  }),
53
96
  },
97
+ // Force the tool call title in OpenCode UI.
98
+ "tool.execute.before": async (input, output) => {
99
+ if (input.tool !== "ask_user_questions")
100
+ return;
101
+ const summary = safeSummaryFromArgs(output.args);
102
+ if (summary)
103
+ titleByCallId.set(input.callID, summary);
104
+ },
105
+ "tool.execute.after": async (input, output) => {
106
+ if (input.tool !== "ask_user_questions")
107
+ return;
108
+ const summary = titleByCallId.get(input.callID);
109
+ if (summary) {
110
+ output.title = summary;
111
+ }
112
+ titleByCallId.delete(input.callID);
113
+ },
54
114
  });
55
115
  export default AskUserQuestionsPlugin;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@paulp-o/opencode-auq",
4
- "version": "1.6.0",
4
+ "version": "2.0.0",
5
5
  "description": "OpenCode plugin that forwards ask_user_questions to the auq CLI",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc -p tsconfig.json",
16
- "prepublishOnly": "npm run build",
16
+ "prepublishOnly": "bun run build",
17
17
  "typecheck": "tsc -p tsconfig.json --noEmit"
18
18
  },
19
19
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  "@opencode-ai/plugin": ">=1.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@opencode-ai/plugin": "^1.1.3",
38
+ "@opencode-ai/plugin": "^1.1.25",
39
39
  "@tsconfig/node22": "^22.0.1",
40
40
  "@types/node": "^22.13.0",
41
41
  "typescript": "^5.8.3"