@wingman-ai/gateway 0.5.4 → 0.6.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 (41) hide show
  1. package/dist/agent/backend/filtered-backend.cjs +130 -0
  2. package/dist/agent/backend/filtered-backend.d.ts +10 -0
  3. package/dist/agent/backend/filtered-backend.js +87 -0
  4. package/dist/agent/middleware/additional-messages.cjs +4 -1
  5. package/dist/agent/middleware/additional-messages.js +4 -1
  6. package/dist/agent/tools/browser_control.cjs +1 -1
  7. package/dist/agent/tools/browser_control.js +1 -1
  8. package/dist/agent/tools/browser_runtime.cjs +184 -15
  9. package/dist/agent/tools/browser_runtime.d.ts +59 -2
  10. package/dist/agent/tools/browser_runtime.js +182 -16
  11. package/dist/agent/tools/browser_session.cjs +44 -8
  12. package/dist/agent/tools/browser_session.d.ts +68 -123
  13. package/dist/agent/tools/browser_session.js +45 -9
  14. package/dist/agent/tools/browser_session_manager.cjs +15 -4
  15. package/dist/agent/tools/browser_session_manager.d.ts +8 -2
  16. package/dist/agent/tools/browser_session_manager.js +15 -4
  17. package/dist/cli/commands/init.cjs +80 -102
  18. package/dist/cli/commands/init.js +80 -102
  19. package/dist/cli/core/agentInvoker.cjs +4 -2
  20. package/dist/cli/core/agentInvoker.js +4 -2
  21. package/dist/cli/core/sessionManager.cjs +208 -41
  22. package/dist/cli/core/sessionManager.d.ts +20 -0
  23. package/dist/cli/core/sessionManager.js +208 -41
  24. package/dist/cli/index.cjs +16 -1
  25. package/dist/cli/index.js +16 -1
  26. package/dist/cli/services/updateCheck.cjs +212 -0
  27. package/dist/cli/services/updateCheck.d.ts +26 -0
  28. package/dist/cli/services/updateCheck.js +166 -0
  29. package/dist/gateway/server.cjs +7 -0
  30. package/dist/gateway/server.js +7 -0
  31. package/dist/webui/assets/index-D3x3G75t.css +11 -0
  32. package/dist/webui/assets/index-UpMmcU1f.js +215 -0
  33. package/dist/webui/index.html +2 -2
  34. package/package.json +3 -3
  35. package/templates/agents/README.md +1 -0
  36. package/templates/agents/game-dev/agent.md +1 -0
  37. package/templates/agents/main/agent.md +3 -3
  38. package/templates/agents/researcher/agent.md +5 -5
  39. package/dist/webui/assets/index-XrEnkZiq.css +0 -11
  40. package/dist/webui/assets/index-mDs6HbKM.js +0 -215
  41. package/dist/webui/assets/wingman_logo-Cogyt3qm.webp +0 -0
@@ -1,11 +1,43 @@
1
1
  import { tool } from "langchain";
2
2
  import { object, string } from "zod";
3
- import { BrowserControlInputSchema, BrowserSessionActionInputSchema } from "./browser_runtime.js";
3
+ import { BrowserControlInputSchema, BrowserSessionActionInputSchema, BrowserSessionVideoRecordingSchema } from "./browser_runtime.js";
4
4
  const CloseBrowserSessionInputSchema = object({
5
5
  session_id: string().min(1).describe("Existing browser session id to close")
6
6
  });
7
7
  const BrowserSessionListInputSchema = object({});
8
- const buildResponse = (payload)=>payload;
8
+ const BrowserSessionStartInputSchema = BrowserControlInputSchema.extend({
9
+ recordVideo: BrowserSessionVideoRecordingSchema.optional().describe("Enable Playwright video recording for this managed session. Videos are finalized and returned when the session closes.")
10
+ });
11
+ const buildMediaBlocks = (media)=>media.map((entry)=>{
12
+ if (!entry || "object" != typeof entry) return null;
13
+ const record = entry;
14
+ const uri = "string" == typeof record.url && record.url.trim() ? record.url.trim() : "string" == typeof record.uri && record.uri.trim() ? record.uri.trim() : "";
15
+ if (!uri) return null;
16
+ return {
17
+ type: "resource_link",
18
+ uri,
19
+ ..."string" == typeof record.mimeType ? {
20
+ mimeType: record.mimeType
21
+ } : {},
22
+ ..."string" == typeof record.name ? {
23
+ name: record.name
24
+ } : {}
25
+ };
26
+ }).filter(Boolean);
27
+ const buildResponse = (payload)=>{
28
+ const media = Array.isArray(payload.media) ? payload.media : [];
29
+ return {
30
+ ...payload,
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: JSON.stringify(payload, null, 2)
35
+ },
36
+ ...buildMediaBlocks(media)
37
+ ],
38
+ structuredContent: payload
39
+ };
40
+ };
9
41
  const summarizeResult = (snapshot, summary)=>({
10
42
  session_id: snapshot.sessionId,
11
43
  status: snapshot.status,
@@ -25,6 +57,8 @@ const summarizeResult = (snapshot, summary)=>({
25
57
  fallback_reason: summary?.fallbackReason || null,
26
58
  extensions: summary?.extensions || [],
27
59
  action_results: summary?.actionResults || [],
60
+ media: summary?.media || [],
61
+ video_recording: summary?.videoRecording || null,
28
62
  browser: summary?.browser || null,
29
63
  transport: summary?.transport || snapshot.transportUsed
30
64
  });
@@ -48,8 +82,8 @@ const createBrowserSessionStartTool = (options, dependencies = {})=>tool(async (
48
82
  }
49
83
  }, {
50
84
  name: "browser_session_start",
51
- description: 'Start a managed browser session that persists across multiple tool calls. Use this for iterative QA/debugging where the same browser state should survive across turns. Transport can be selected per session with "auto", "playwright", or "relay".',
52
- schema: BrowserControlInputSchema
85
+ description: 'Default browser automation entrypoint. Start a managed browser session for screenshots, extraction, QA, and multi-step automation; you can use it for one-shot tasks or continue the same browser state across multiple tool calls. Use browser_session_action to continue and browser_session_close when finished. Transport can be selected per session with "auto", "playwright", or "relay".',
86
+ schema: BrowserSessionStartInputSchema
53
87
  });
54
88
  const createBrowserSessionActionTool = (options)=>tool(async (input)=>{
55
89
  try {
@@ -71,19 +105,21 @@ const createBrowserSessionActionTool = (options)=>tool(async (input)=>{
71
105
  }
72
106
  }, {
73
107
  name: "browser_session_action",
74
- description: "Run browser actions inside an existing managed browser session. Use the session_id returned by browser_session_start to continue the same tab/profile state across multiple calls.",
108
+ description: "Continue the default managed browser workflow by running more actions inside an existing browser session. Use the session_id returned by browser_session_start to keep the same tab/profile state across multiple calls.",
75
109
  schema: BrowserSessionActionInputSchema
76
110
  });
77
111
  const createBrowserSessionCloseTool = (options)=>tool(async ({ session_id })=>{
78
112
  try {
79
- const snapshot = await options.sessionManager.closeSession({
113
+ const { snapshot, closeSummary } = await options.sessionManager.closeSession({
80
114
  ownerId: options.ownerId,
81
115
  sessionId: session_id
82
116
  });
83
117
  return buildResponse({
84
118
  ok: true,
85
119
  closed: true,
86
- ...summarizeResult(snapshot, null)
120
+ ...summarizeResult(snapshot, null),
121
+ media: closeSummary.media,
122
+ video_recording: closeSummary.videoRecording
87
123
  });
88
124
  } catch (error) {
89
125
  return buildResponse({
@@ -93,7 +129,7 @@ const createBrowserSessionCloseTool = (options)=>tool(async ({ session_id })=>{
93
129
  }
94
130
  }, {
95
131
  name: "browser_session_close",
96
- description: "Close a managed browser session and release any temporary browser resources or profile locks owned by that session.",
132
+ description: "Finish the managed browser workflow by closing a session and releasing any temporary browser resources or profile locks owned by it. Finalized video recordings are returned here.",
97
133
  schema: CloseBrowserSessionInputSchema
98
134
  });
99
135
  const createBrowserSessionListTool = (options)=>tool(async ()=>{
@@ -104,7 +140,7 @@ const createBrowserSessionListTool = (options)=>tool(async ()=>{
104
140
  });
105
141
  }, {
106
142
  name: "browser_session_list",
107
- description: "List the managed browser sessions currently owned by this agent run. Use this to recover a session_id before continuing or closing a session.",
143
+ description: "List the managed browser sessions currently owned by this agent run. Use this to recover a session_id before continuing or closing the default browser workflow.",
108
144
  schema: BrowserSessionListInputSchema
109
145
  });
110
146
  export { createBrowserSessionActionTool, createBrowserSessionCloseTool, createBrowserSessionListTool, createBrowserSessionStartTool };
@@ -99,8 +99,11 @@ class BrowserSessionManager {
99
99
  async closeSession(input) {
100
100
  const record = this.getOwnedRecord(input.ownerId, input.sessionId);
101
101
  const snapshot = this.toSnapshot(record);
102
- await this.closeAndDelete(record);
103
- return snapshot;
102
+ const closeSummary = await this.closeAndDelete(record);
103
+ return {
104
+ snapshot,
105
+ closeSummary
106
+ };
104
107
  }
105
108
  listSessions(ownerId) {
106
109
  const normalizedOwnerId = this.normalizeOwnerId(ownerId);
@@ -118,10 +121,17 @@ class BrowserSessionManager {
118
121
  await Promise.allSettled(expired.map((record)=>this.closeAndDelete(record)));
119
122
  }
120
123
  async closeAndDelete(record) {
121
- if (record.closing) return;
124
+ if (record.closing) return {
125
+ media: [],
126
+ videoRecording: null
127
+ };
122
128
  record.closing = true;
129
+ let closeSummary = {
130
+ media: [],
131
+ videoRecording: null
132
+ };
123
133
  try {
124
- await (0, external_browser_runtime_cjs_namespaceObject.closeBrowserSessionRuntime)(record.runtime, record.dependencies);
134
+ closeSummary = await (0, external_browser_runtime_cjs_namespaceObject.closeBrowserSessionRuntime)(record.runtime, record.dependencies);
125
135
  } finally{
126
136
  this.sessions.delete(record.sessionId);
127
137
  const ownerSessions = this.ownerIndex.get(record.ownerId);
@@ -130,6 +140,7 @@ class BrowserSessionManager {
130
140
  if (0 === ownerSessions.size) this.ownerIndex.delete(record.ownerId);
131
141
  }
132
142
  }
143
+ return closeSummary;
133
144
  }
134
145
  getOwnedRecord(ownerId, sessionId) {
135
146
  const normalizedOwnerId = this.normalizeOwnerId(ownerId);
@@ -1,4 +1,4 @@
1
- import { type BrowserControlDependencies, type BrowserControlInput, type BrowserControlToolOptions, type BrowserExecutionSummary, type BrowserRuntimeTransport, type BrowserTransportPreference } from "./browser_runtime.js";
1
+ import { type BrowserControlDependencies, type BrowserControlInput, type BrowserControlToolOptions, type BrowserExecutionSummary, type BrowserGeneratedMedia, type BrowserVideoRecordingSummary, type BrowserRuntimeTransport, type BrowserTransportPreference } from "./browser_runtime.js";
2
2
  export interface BrowserSessionSnapshot {
3
3
  sessionId: string;
4
4
  ownerId: string;
@@ -52,7 +52,13 @@ export declare class BrowserSessionManager {
52
52
  snapshot: BrowserSessionSnapshot;
53
53
  summary: BrowserExecutionSummary;
54
54
  }>;
55
- closeSession(input: CloseBrowserSessionInput): Promise<BrowserSessionSnapshot>;
55
+ closeSession(input: CloseBrowserSessionInput): Promise<{
56
+ snapshot: BrowserSessionSnapshot;
57
+ closeSummary: {
58
+ media: BrowserGeneratedMedia[];
59
+ videoRecording: BrowserVideoRecordingSummary | null;
60
+ };
61
+ }>;
56
62
  listSessions(ownerId: string): BrowserSessionSnapshot[];
57
63
  private cleanupExpiredSessions;
58
64
  private closeAndDelete;
@@ -70,8 +70,11 @@ class BrowserSessionManager {
70
70
  async closeSession(input) {
71
71
  const record = this.getOwnedRecord(input.ownerId, input.sessionId);
72
72
  const snapshot = this.toSnapshot(record);
73
- await this.closeAndDelete(record);
74
- return snapshot;
73
+ const closeSummary = await this.closeAndDelete(record);
74
+ return {
75
+ snapshot,
76
+ closeSummary
77
+ };
75
78
  }
76
79
  listSessions(ownerId) {
77
80
  const normalizedOwnerId = this.normalizeOwnerId(ownerId);
@@ -89,10 +92,17 @@ class BrowserSessionManager {
89
92
  await Promise.allSettled(expired.map((record)=>this.closeAndDelete(record)));
90
93
  }
91
94
  async closeAndDelete(record) {
92
- if (record.closing) return;
95
+ if (record.closing) return {
96
+ media: [],
97
+ videoRecording: null
98
+ };
93
99
  record.closing = true;
100
+ let closeSummary = {
101
+ media: [],
102
+ videoRecording: null
103
+ };
94
104
  try {
95
- await closeBrowserSessionRuntime(record.runtime, record.dependencies);
105
+ closeSummary = await closeBrowserSessionRuntime(record.runtime, record.dependencies);
96
106
  } finally{
97
107
  this.sessions.delete(record.sessionId);
98
108
  const ownerSessions = this.ownerIndex.get(record.ownerId);
@@ -101,6 +111,7 @@ class BrowserSessionManager {
101
111
  if (0 === ownerSessions.size) this.ownerIndex.delete(record.ownerId);
102
112
  }
103
113
  }
114
+ return closeSummary;
104
115
  }
105
116
  getOwnedRecord(ownerId, sessionId) {
106
117
  const normalizedOwnerId = this.normalizeOwnerId(ownerId);
@@ -44,41 +44,19 @@ const external_node_url_namespaceObject = require("node:url");
44
44
  const prompts_namespaceObject = require("@clack/prompts");
45
45
  const external_chalk_namespaceObject = require("chalk");
46
46
  var external_chalk_default = /*#__PURE__*/ __webpack_require__.n(external_chalk_namespaceObject);
47
+ const external_js_yaml_namespaceObject = require("js-yaml");
47
48
  const modelFactory_cjs_namespaceObject = require("../../agent/config/modelFactory.cjs");
48
49
  const external_logger_cjs_namespaceObject = require("../../logger.cjs");
49
50
  const credentials_cjs_namespaceObject = require("../../providers/credentials.cjs");
50
51
  const registry_cjs_namespaceObject = require("../../providers/registry.cjs");
51
52
  const schema_cjs_namespaceObject = require("../config/schema.cjs");
52
53
  const outputManager_cjs_namespaceObject = require("../core/outputManager.cjs");
53
- const DEFAULT_AGENT_ID = "wingman";
54
- const DEFAULT_AGENT_DESCRIPTION = "General-purpose coding assistant for this workspace.";
55
- const DEFAULT_AGENT_PROMPT = "You are Wingman, a coding assistant for this repository.\nBe direct and concise. Ask clarifying questions when requirements are unclear.\nPrefer minimal diffs and safe changes. Avoid destructive actions unless asked.\nUse tools to inspect the codebase before editing.\nUse browser_session_start/browser_session_action/browser_session_close for iterative browser debugging across turns, and browser_control for one-shot browser tasks.";
56
- const DEFAULT_TOOLS = [
57
- "code_search",
58
- "git_status",
59
- "command_execute",
60
- "browser_control",
61
- "browser_session_start",
62
- "browser_session_action",
63
- "browser_session_close",
64
- "browser_session_list",
65
- "internet_search",
66
- "think"
67
- ];
68
54
  const DEFAULT_FS_ROOT = ".";
69
55
  const DEFAULT_BROWSER_PROFILE_ID = "default";
70
56
  const DEFAULT_BROWSER_PROFILES_DIR = ".wingman/browser-profiles";
71
57
  const DEFAULT_BROWSER_EXTENSIONS_DIR = ".wingman/browser-extensions";
72
58
  const DEFAULT_BUNDLED_EXTENSION_ID = "wingman";
73
59
  const DEFAULT_BROWSER_TRANSPORT = "auto";
74
- const DEFAULT_MODELS = {
75
- anthropic: "anthropic:claude-sonnet-4-5",
76
- openai: "openai:gpt-4o",
77
- codex: "codex:codex-mini-latest",
78
- openrouter: "openrouter:openai/gpt-4o",
79
- copilot: "copilot:gpt-4o",
80
- xai: "xai:grok-beta"
81
- };
82
60
  const DEFAULT_INIT_MODE = "onboard";
83
61
  const DEFAULT_ONBOARD_COMPONENTS = [
84
62
  "config",
@@ -137,8 +115,20 @@ async function executeInitCommand(args, options = {}) {
137
115
  renderInitBanner(outputManager, nonInteractive, mode, components);
138
116
  const bundledAgentsPath = resolveBundledAgentsPath();
139
117
  const bundledAgents = bundledAgentsPath ? listBundledAgents(bundledAgentsPath) : [];
140
- const explicitAgent = Boolean(args.agent?.trim()) || Boolean(args.subcommand) && !isHelpCommand(args.subcommand) && !args.subcommand.startsWith("-");
141
- const defaultAgentId = resolveAgentId(args);
118
+ const requestedAgentId = resolveRequestedAgentId(args);
119
+ const explicitAgent = Boolean(requestedAgentId);
120
+ const resolvedAgentId = resolveDefaultAgentId({
121
+ requestedAgentId,
122
+ bundledAgents,
123
+ configPath
124
+ });
125
+ if (!resolvedAgentId && (runConfig || runAgents)) throw new Error("No explicit agents are available. Add an agent under .wingman/agents or pass --agent with an existing bundled agent.");
126
+ const defaultAgentId = resolvedAgentId || "main";
127
+ if (runConfig || runAgents) assertAgentIsExplicit({
128
+ agentId: defaultAgentId,
129
+ bundledAgents,
130
+ configRoot
131
+ });
142
132
  const agentPlan = runConfig || runAgents ? syncAgentsOnly ? resolveAgentSyncPlan({
143
133
  defaultAgentId,
144
134
  bundledAgents,
@@ -163,7 +153,6 @@ async function executeInitCommand(args, options = {}) {
163
153
  const model = shouldResolveModel ? await resolveModelSelection({
164
154
  nonInteractive,
165
155
  optionMap,
166
- providerName,
167
156
  outputManager
168
157
  }) : void 0;
169
158
  if (runConfig) await runStep(useClack, "Writing workspace config", async ()=>handleConfigSetup({
@@ -182,7 +171,6 @@ async function executeInitCommand(args, options = {}) {
182
171
  agentId: agentPlan.defaultAgentId,
183
172
  model,
184
173
  force,
185
- nonInteractive,
186
174
  outputManager,
187
175
  bundledAgentsPath,
188
176
  copyAgents: agentPlan.copyAgents,
@@ -249,10 +237,35 @@ function isHelpCommand(subcommand) {
249
237
  "-h"
250
238
  ].includes(subcommand);
251
239
  }
252
- function resolveAgentId(args) {
253
- if (args.agent?.trim()) return args.agent.trim();
254
- if (args.subcommand && !isHelpCommand(args.subcommand) && !args.subcommand.startsWith("-")) return args.subcommand.trim();
255
- return DEFAULT_AGENT_ID;
240
+ function resolveRequestedAgentId(args) {
241
+ if (args.agent?.trim()) return sanitizeAgentId(args.agent.trim());
242
+ if (args.subcommand && !isHelpCommand(args.subcommand) && !args.subcommand.startsWith("-")) return sanitizeAgentId(args.subcommand.trim());
243
+ }
244
+ function resolveDefaultAgentId(input) {
245
+ const { requestedAgentId, bundledAgents, configPath } = input;
246
+ if (requestedAgentId) return requestedAgentId;
247
+ const existingDefaultAgent = readConfiguredDefaultAgent(configPath);
248
+ if (existingDefaultAgent) return existingDefaultAgent;
249
+ const preferredBundledAgent = resolvePreferredBundledAgentId(bundledAgents);
250
+ if (preferredBundledAgent) return preferredBundledAgent;
251
+ }
252
+ function readConfiguredDefaultAgent(configPath) {
253
+ if (!(0, external_node_fs_namespaceObject.existsSync)(configPath)) return;
254
+ try {
255
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(configPath, "utf-8");
256
+ const parsed = JSON.parse(raw);
257
+ if ("string" == typeof parsed.defaultAgent && parsed.defaultAgent.trim()) return sanitizeAgentId(parsed.defaultAgent.trim());
258
+ } catch {}
259
+ }
260
+ function resolvePreferredBundledAgentId(bundledAgents) {
261
+ if (bundledAgents.includes("main")) return "main";
262
+ return bundledAgents[0];
263
+ }
264
+ function assertAgentIsExplicit(input) {
265
+ const { agentId, bundledAgents, configRoot } = input;
266
+ if (bundledAgents.includes(agentId)) return;
267
+ if (findAgentConfigPath(configRoot, agentId)) return;
268
+ throw new Error(`Agent "${agentId}" is not defined. Choose one of the bundled agents (${bundledAgents.join(", ")}) or create .wingman/agents/${agentId}/agent.md explicitly.`);
256
269
  }
257
270
  async function resolveAgentPlan(input) {
258
271
  const { explicitAgent, defaultAgentId, bundledAgents, nonInteractive, optionMap, outputManager, ensureDefaultAgentInSelection } = input;
@@ -512,7 +525,7 @@ function bootstrapBrowserDefaults(configRoot, config) {
512
525
  };
513
526
  }
514
527
  async function handleAgentSetup(input) {
515
- const { configRoot, agentId, model, force, nonInteractive, outputManager, bundledAgentsPath, copyAgents, ensureDefaultAgent = true } = input;
528
+ const { configRoot, agentId, model, force, outputManager, bundledAgentsPath, copyAgents, ensureDefaultAgent = true } = input;
516
529
  const copiedAgents = bundledAgentsPath ? copyBundledAgents({
517
530
  bundledAgentsPath,
518
531
  configRoot,
@@ -522,17 +535,8 @@ async function handleAgentSetup(input) {
522
535
  }) : new Set();
523
536
  if (copiedAgents.size > 0) writeLine(outputManager, `Copied ${copiedAgents.size} bundled agent(s) to ${(0, external_node_path_namespaceObject.join)(configRoot, "agents")}`);
524
537
  if (!ensureDefaultAgent) return;
525
- const expectedAgentDir = (0, external_node_path_namespaceObject.join)(configRoot, "agents", agentId);
526
- const expectedAgentPath = (0, external_node_path_namespaceObject.join)(expectedAgentDir, "agent.json");
527
- const expectedAgentExists = (0, external_node_fs_namespaceObject.existsSync)(expectedAgentPath);
528
- if (!expectedAgentExists) return void await createFallbackAgent({
529
- configRoot,
530
- agentId,
531
- model,
532
- force,
533
- nonInteractive,
534
- outputManager
535
- });
538
+ const expectedAgentPath = findAgentConfigPath(configRoot, agentId);
539
+ if (!expectedAgentPath) throw new Error(`Agent "${agentId}" was not found after init. Create .wingman/agents/${agentId}/agent.md explicitly or choose a bundled agent.`);
536
540
  if (model) applyModelToAgent(expectedAgentPath, model, outputManager);
537
541
  }
538
542
  function resolveBundledAgentsPath() {
@@ -589,35 +593,34 @@ function copyDirectory(source, target) {
589
593
  else (0, external_node_fs_namespaceObject.copyFileSync)(sourcePath, targetPath);
590
594
  }
591
595
  }
592
- async function createFallbackAgent(input) {
593
- const { configRoot, agentId, model, force, nonInteractive, outputManager } = input;
596
+ function findAgentConfigPath(configRoot, agentId) {
594
597
  const agentDir = (0, external_node_path_namespaceObject.join)(configRoot, "agents", agentId);
595
- const agentPath = (0, external_node_path_namespaceObject.join)(agentDir, "agent.json");
596
- const agentExists = (0, external_node_fs_namespaceObject.existsSync)(agentPath);
597
- if (agentExists && !force) {
598
- if (nonInteractive) return void writeLine(outputManager, `Agent "${agentId}" already exists. Use --force to overwrite.`);
599
- const shouldOverwrite = await promptConfirm(`Agent "${agentId}" exists. Overwrite? (y/N): `, false);
600
- if (!shouldOverwrite) return void writeLine(outputManager, `Keeping existing agent "${agentId}".`);
601
- }
602
- (0, external_node_fs_namespaceObject.mkdirSync)(agentDir, {
603
- recursive: true
604
- });
605
- const agentConfig = {
606
- name: agentId,
607
- description: DEFAULT_AGENT_DESCRIPTION,
608
- systemPrompt: DEFAULT_AGENT_PROMPT,
609
- tools: DEFAULT_TOOLS
610
- };
611
- if (model) agentConfig.model = model;
612
- (0, external_node_fs_namespaceObject.writeFileSync)(agentPath, JSON.stringify(agentConfig, null, 2));
613
- writeLine(outputManager, `Created starter agent at ${agentPath}`);
598
+ const jsonPath = (0, external_node_path_namespaceObject.join)(agentDir, "agent.json");
599
+ if ((0, external_node_fs_namespaceObject.existsSync)(jsonPath)) return jsonPath;
600
+ const markdownPath = (0, external_node_path_namespaceObject.join)(agentDir, "agent.md");
601
+ if ((0, external_node_fs_namespaceObject.existsSync)(markdownPath)) return markdownPath;
602
+ return null;
614
603
  }
615
604
  function applyModelToAgent(agentPath, model, outputManager) {
616
605
  try {
617
- const raw = (0, external_node_fs_namespaceObject.readFileSync)(agentPath, "utf-8");
618
- const parsed = JSON.parse(raw);
619
- parsed.model = model;
620
- (0, external_node_fs_namespaceObject.writeFileSync)(agentPath, JSON.stringify(parsed, null, 2));
606
+ if (agentPath.endsWith(".json")) {
607
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(agentPath, "utf-8");
608
+ const parsed = JSON.parse(raw);
609
+ parsed.model = model;
610
+ (0, external_node_fs_namespaceObject.writeFileSync)(agentPath, JSON.stringify(parsed, null, 2));
611
+ } else if (agentPath.endsWith(".md")) {
612
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(agentPath, "utf-8");
613
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
614
+ const match = raw.match(frontmatterRegex);
615
+ if (!match) throw new Error("Invalid agent.md format: missing frontmatter");
616
+ const [, rawFrontmatter, prompt] = match;
617
+ const metadata = external_js_yaml_namespaceObject.load(rawFrontmatter) || {};
618
+ metadata.model = model;
619
+ const serializedFrontmatter = external_js_yaml_namespaceObject.dump(metadata, {
620
+ lineWidth: 120
621
+ });
622
+ (0, external_node_fs_namespaceObject.writeFileSync)(agentPath, `---\n${serializedFrontmatter}---\n\n${prompt?.trim() || ""}\n`);
623
+ } else throw new Error("Unsupported agent config format");
621
624
  writeLine(outputManager, `Updated ${agentPath} with model ${model}`);
622
625
  } catch {
623
626
  writeLine(outputManager, `Unable to update model for ${agentPath}. Update manually.`);
@@ -657,7 +660,7 @@ async function resolveProviderSelection(input) {
657
660
  return normalized;
658
661
  }
659
662
  async function resolveModelSelection(input) {
660
- const { nonInteractive, optionMap, providerName, outputManager } = input;
663
+ const { nonInteractive, optionMap, outputManager } = input;
661
664
  const explicitModel = getStringOption(optionMap, [
662
665
  "model"
663
666
  ]);
@@ -665,22 +668,13 @@ async function resolveModelSelection(input) {
665
668
  validateModel(explicitModel);
666
669
  return explicitModel;
667
670
  }
668
- const providers = (0, registry_cjs_namespaceObject.listProviderSpecs)("model");
669
- const configuredProvider = providers.find((provider)=>"missing" !== (0, credentials_cjs_namespaceObject.resolveProviderToken)(provider.name).source);
670
- const suggestedProvider = providerName || configuredProvider?.name;
671
- const suggestedModel = suggestedProvider ? DEFAULT_MODELS[suggestedProvider] : void 0;
672
- if (nonInteractive) {
673
- if (suggestedModel) return suggestedModel;
674
- return;
675
- }
671
+ if (nonInteractive) return;
676
672
  const inputValue = await (0, prompts_namespaceObject.text)({
677
673
  message: "Model string (provider:model)",
678
- placeholder: suggestedModel ? void 0 : "anthropic:claude-sonnet-4-5",
679
- defaultValue: suggestedModel
674
+ placeholder: "anthropic:claude-sonnet-4-5"
680
675
  });
681
676
  if ((0, prompts_namespaceObject.isCancel)(inputValue)) abortSetup();
682
677
  const trimmed = String(inputValue ?? "").trim();
683
- if (!trimmed && suggestedModel) return suggestedModel;
684
678
  if (!trimmed) return void writeLine(outputManager, "Skipping model selection.");
685
679
  validateModel(trimmed);
686
680
  return trimmed;
@@ -783,29 +777,13 @@ async function promptForDefaultAgent(agents, currentDefault) {
783
777
  const defaultValue = choices.includes(currentDefault) ? currentDefault : choices[0];
784
778
  const selection = await (0, prompts_namespaceObject.select)({
785
779
  message: "Pick a default agent",
786
- options: [
787
- ...choices.map((agent)=>({
788
- value: agent,
789
- label: agent
790
- })),
791
- {
792
- value: "__custom__",
793
- label: "Custom agent name"
794
- }
795
- ],
780
+ options: choices.map((agent)=>({
781
+ value: agent,
782
+ label: agent
783
+ })),
796
784
  initialValue: defaultValue
797
785
  });
798
786
  if ((0, prompts_namespaceObject.isCancel)(selection)) abortSetup();
799
- if ("__custom__" === selection) {
800
- const input = await (0, prompts_namespaceObject.text)({
801
- message: "Default agent name",
802
- placeholder: defaultValue
803
- });
804
- if ((0, prompts_namespaceObject.isCancel)(input)) abortSetup();
805
- const trimmed = String(input ?? "").trim();
806
- if (!trimmed) return defaultValue;
807
- return sanitizeAgentId(trimmed);
808
- }
809
787
  return sanitizeAgentId(String(selection));
810
788
  }
811
789
  async function promptForAgentSelection(agents, defaultAgent) {
@@ -872,12 +850,12 @@ Usage:
872
850
  wingman init <agent-name>
873
851
 
874
852
  Options:
875
- --agent <name> Agent name (default: wingman)
853
+ --agent <name> Bundled or existing explicit agent name
876
854
  --mode <name> Init mode (onboard|sync). Default: onboard
877
855
  --only <targets> Run only selected setup targets (config,agents,provider)
878
856
  --agents <list> Copy only these bundled agents (comma-separated)
879
857
  --model <provider:model>
880
- Set model for the starter agent
858
+ Set model for the selected explicit agent
881
859
  --provider <name> Provider to configure (anthropic|openai|codex|openrouter|copilot|xai)
882
860
  --token <token> Save provider token (non-interactive)
883
861
  --api-key <key> Alias for --token