@netlify/agent-runner-cli 1.151.0 → 1.152.0-EX-2670.1

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.
@@ -9,6 +9,11 @@ description:
9
9
 
10
10
  When a decision is genuinely the user's to make and would change what you build, ask rather than assume.
11
11
 
12
- Use the `ask` tool: pass one to three questions, each with a short `header`, the `question` text, and `options` when
13
- there is a clear set of choices. Calling it pauses the process until the user answers, so ask everything you need in a
14
- single call and stop. A plain-text question does not reach the user.
12
+ Use the `ask` tool: pass one to three questions, each with a short `header`, the `question` text, and two or three
13
+ `options` when there is a clear set of choices. Add `multiSelect: true` when several of those options can apply at once.
14
+ Never write an option like "Other", "Something else", or "None of these". The UI adds an "Other" choice and a free-text
15
+ field to give extra context to every question that has options, so if you added an "other" option it would be a
16
+ duplicate. Leave `options` out to ask an open question the user answers in their own words.
17
+
18
+ Calling the tool pauses the process until the user answers, so ask everything you need in a single call and stop. A
19
+ plain-text question does not reach the user.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.151.0",
4
+ "version": "1.152.0-EX-2670.1",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -0,0 +1,71 @@
1
+ import { readFileSync, appendFileSync, writeFileSync, existsSync } from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
5
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
6
+ import { createJiti } from 'jiti'
7
+
8
+ const jiti = createJiti(import.meta.url, { interopDefault: true })
9
+ const { ASK_TOOL_DESCRIPTION, askQuestionsShape, formatAskAnswerBlock } = await jiti.import(
10
+ '../../src/lib/ask-schema.js',
11
+ )
12
+ const {
13
+ ASK_LOG_FILENAME,
14
+ ASK_SERVER_READY_FILENAME,
15
+ CANNED_ANSWERS_FILENAME,
16
+ INTERACTIONS_MCP_SERVER,
17
+ UNANTICIPATED_QUESTION_ANSWER,
18
+ } = await jiti.import('../../scenarios/helpers/interactions-stub-contract.js')
19
+
20
+ const WORKSPACE = process.cwd()
21
+ const CANNED_ANSWERS_FILE = path.join(WORKSPACE, CANNED_ANSWERS_FILENAME)
22
+ const ASK_LOG_FILE = path.join(WORKSPACE, ASK_LOG_FILENAME)
23
+ const ASK_SERVER_READY_FILE = path.join(WORKSPACE, ASK_SERVER_READY_FILENAME)
24
+
25
+ const PREAMBLE = 'The user answered, and the run has resumed — the pause is over.'
26
+
27
+ const loadCannedAnswersOrThrow = () => {
28
+ if (!existsSync(CANNED_ANSWERS_FILE)) {
29
+ throw new Error(
30
+ `${CANNED_ANSWERS_FILE} is missing. scenarioCannedAnswersSetup should have written it into the workspace (cwd=${WORKSPACE}).`,
31
+ )
32
+ }
33
+ const parsed = JSON.parse(readFileSync(CANNED_ANSWERS_FILE, 'utf8'))
34
+ if (!Array.isArray(parsed.answers) || parsed.answers.length === 0) {
35
+ throw new Error(`${CANNED_ANSWERS_FILE} has no non-empty "answers" array.`)
36
+ }
37
+ return parsed.answers
38
+ }
39
+
40
+ const logAskCall = (questions) => {
41
+ appendFileSync(ASK_LOG_FILE, `${JSON.stringify({ at: new Date().toISOString(), questions })}\n`)
42
+ }
43
+
44
+ const server = new McpServer({ name: INTERACTIONS_MCP_SERVER, version: '1.0.0' })
45
+
46
+ server.registerTool(
47
+ 'ask',
48
+ {
49
+ description: ASK_TOOL_DESCRIPTION,
50
+ inputSchema: askQuestionsShape,
51
+ },
52
+ ({ questions }) => {
53
+ logAskCall(questions)
54
+ const answersByPosition = loadCannedAnswersOrThrow()
55
+ const pairs = questions.map((question, index) => ({
56
+ question: question.question,
57
+ answer: answersByPosition[index] ?? UNANTICIPATED_QUESTION_ANSWER,
58
+ }))
59
+ return { content: [{ type: 'text', text: formatAskAnswerBlock(pairs, { preamble: PREAMBLE }) }] }
60
+ },
61
+ )
62
+
63
+ const shutdown = () => process.exit(0)
64
+ process.stdin.on('close', shutdown)
65
+ process.stdin.on('end', shutdown)
66
+
67
+ loadCannedAnswersOrThrow()
68
+
69
+ await server.connect(new StdioServerTransport())
70
+
71
+ writeFileSync(ASK_SERVER_READY_FILE, `${new Date().toISOString()}\n`)