@vrdmr/fnx-test 0.4.3 → 0.5.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/lib/azurite-manager.js +67 -9
- package/lib/chat/index.js +281 -0
- package/lib/cli.js +24 -0
- package/lib/config.js +2 -8
- package/lib/host-launcher.js +121 -30
- package/lib/init/manifest.js +29 -10
- package/lib/init/prompts.js +459 -69
- package/lib/init/scaffold.js +56 -36
- package/lib/init.js +151 -10
- package/lib/runtimes.js +11 -22
- package/lib/setup/agent-detect.js +92 -0
- package/lib/setup/detect.js +117 -0
- package/lib/setup/index.js +572 -0
- package/lib/utils.js +27 -0
- package/manifests/default.yaml +124 -0
- package/manifests/skills/fnx-best-practices/SKILL.md +64 -0
- package/manifests/skills/fnx-best-practices/references/azure-functions-docs.md +60 -0
- package/manifests/skills/fnx-best-practices/references/fnx-specific.md +97 -0
- package/manifests/skills/fnx-create-function/SKILL.md +133 -0
- package/manifests/skills/fnx-create-function/references/templates.md +105 -0
- package/manifests/skills/fnx-diagnostics/SKILL.md +84 -0
- package/manifests/skills/fnx-diagnostics/references/diagnostic-checklist.md +59 -0
- package/manifests/skills/fnx-diagnostics/references/fnx-error-patterns.md +71 -0
- package/manifests/skills/fnx-feedback/SKILL.md +142 -0
- package/manifests/skills/fnx-intro/SKILL.md +83 -0
- package/manifests/skills/fnx-intro/references/fnx-commands.md +157 -0
- package/manifests/skills/fnx-intro/references/roadmap.md +60 -0
- package/package.json +3 -1
- package/profiles/sku-profiles.json +6 -6
package/lib/azurite-manager.js
CHANGED
|
@@ -14,16 +14,65 @@ let azuriteProcess = null;
|
|
|
14
14
|
let weStartedAzurite = false;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Check if a connection string value indicates development/emulator storage.
|
|
18
|
+
* Matches:
|
|
19
|
+
* - "UseDevelopmentStorage=true"
|
|
20
|
+
* - "UseDevelopmentStorage=true;DevelopmentStorageProxyUri=..."
|
|
21
|
+
* - Connection strings pointing to devstoreaccount1 (Azurite default)
|
|
22
|
+
* - Connection strings pointing to 127.0.0.1:10000 (Azurite default ports)
|
|
19
23
|
*/
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
function isDevStorageConnectionString(value) {
|
|
25
|
+
if (!value || typeof value !== 'string') return false;
|
|
26
|
+
|
|
27
|
+
const normalized = value.toLowerCase();
|
|
28
|
+
|
|
29
|
+
// Check for UseDevelopmentStorage=true (with or without additional params)
|
|
30
|
+
if (normalized.startsWith('usedevelopmentstorage=true')) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check for Azurite default account name
|
|
35
|
+
if (normalized.includes('devstoreaccount1')) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check for localhost Azurite ports (10000, 10001, 10002)
|
|
40
|
+
if (normalized.includes('127.0.0.1:10000') ||
|
|
41
|
+
normalized.includes('127.0.0.1:10001') ||
|
|
42
|
+
normalized.includes('127.0.0.1:10002') ||
|
|
43
|
+
normalized.includes('localhost:10000') ||
|
|
44
|
+
normalized.includes('localhost:10001') ||
|
|
45
|
+
normalized.includes('localhost:10002')) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
return false;
|
|
25
50
|
}
|
|
26
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Determine whether Azurite is needed based on any setting using development storage.
|
|
54
|
+
* Returns { needed: boolean, keys: string[] } where keys are the ones using dev storage.
|
|
55
|
+
*/
|
|
56
|
+
function needsAzurite(mergedValues) {
|
|
57
|
+
if (!mergedValues) return { needed: false, keys: [] };
|
|
58
|
+
|
|
59
|
+
const devStorageKeys = [];
|
|
60
|
+
|
|
61
|
+
for (const [key, value] of Object.entries(mergedValues)) {
|
|
62
|
+
if (isDevStorageConnectionString(value)) {
|
|
63
|
+
devStorageKeys.push(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Also check AzureWebJobsStorage specially - empty/missing means dev storage
|
|
68
|
+
const webJobsStorage = mergedValues.AzureWebJobsStorage;
|
|
69
|
+
if ((!webJobsStorage || webJobsStorage === '') && !devStorageKeys.includes('AzureWebJobsStorage')) {
|
|
70
|
+
devStorageKeys.push('AzureWebJobsStorage');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { needed: devStorageKeys.length > 0, keys: devStorageKeys };
|
|
74
|
+
}
|
|
75
|
+
|
|
27
76
|
/**
|
|
28
77
|
* TCP probe — resolves true if a connection can be established on the given port.
|
|
29
78
|
*/
|
|
@@ -130,12 +179,21 @@ export async function ensureAzurite(mergedValues, opts = {}) {
|
|
|
130
179
|
return null;
|
|
131
180
|
}
|
|
132
181
|
|
|
133
|
-
|
|
182
|
+
const { needed, keys } = needsAzurite(mergedValues);
|
|
183
|
+
if (!needed) {
|
|
134
184
|
return null;
|
|
135
185
|
}
|
|
136
186
|
|
|
137
|
-
|
|
138
|
-
|
|
187
|
+
// Log which connection strings are using dev storage
|
|
188
|
+
if (keys.length === 1) {
|
|
189
|
+
const val = mergedValues?.[keys[0]] || '(empty)';
|
|
190
|
+
console.log(info(`[fnx] Detected ${keys[0]}=${val}`));
|
|
191
|
+
} else {
|
|
192
|
+
console.log(info(`[fnx] Detected ${keys.length} connection strings requiring Azurite:`));
|
|
193
|
+
for (const key of keys) {
|
|
194
|
+
console.log(info(`[fnx] • ${key}`));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
139
197
|
|
|
140
198
|
// Check if Azurite is already running
|
|
141
199
|
if (await isAzuriteRunning()) {
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fnx chat — launch a coding agent with Azure Functions context.
|
|
3
|
+
* Detects available agents, generates .fnx/agent.md with project
|
|
4
|
+
* context, and starts the agent with the right flags.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
9
|
+
import { join, resolve, dirname } from 'node:path';
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { createInterface } from 'node:readline';
|
|
12
|
+
import { detectProject } from '../setup/detect.js';
|
|
13
|
+
import { detectAgents } from '../setup/agent-detect.js';
|
|
14
|
+
import { title, info, funcName, success, error as errorColor, warning, dim, bold } from '../colors.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Agent launcher definitions — how to start each coding agent.
|
|
18
|
+
*/
|
|
19
|
+
const LAUNCHERS = {
|
|
20
|
+
'claude-code': {
|
|
21
|
+
command: 'claude',
|
|
22
|
+
buildArgs: (ctx) => [], // Claude reads CLAUDE.md and .claude/skills/ automatically
|
|
23
|
+
description: 'Claude Code reads .claude/skills/ and CLAUDE.md automatically',
|
|
24
|
+
},
|
|
25
|
+
'github-copilot': {
|
|
26
|
+
command: 'copilot',
|
|
27
|
+
buildArgs: (ctx) => [], // Copilot reads .github/copilot-instructions.md automatically
|
|
28
|
+
description: 'GitHub Copilot reads .github/copilot-instructions.md automatically',
|
|
29
|
+
},
|
|
30
|
+
'codex': {
|
|
31
|
+
command: 'codex',
|
|
32
|
+
buildArgs: (ctx) => [], // Codex reads AGENTS.md automatically
|
|
33
|
+
description: 'Codex reads AGENTS.md automatically',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Run fnx chat.
|
|
39
|
+
* @param {string[]} args - CLI arguments
|
|
40
|
+
*/
|
|
41
|
+
export async function runChat(args) {
|
|
42
|
+
const appPath = resolveAppPath(args);
|
|
43
|
+
const agentFlag = getFlag(args, '--agent');
|
|
44
|
+
const promptFlag = getFlag(args, '--prompt');
|
|
45
|
+
const setupOnly = args.includes('--setup-only');
|
|
46
|
+
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(title('fnx chat') + dim(' — AI-assisted Azure Functions development'));
|
|
49
|
+
console.log();
|
|
50
|
+
|
|
51
|
+
// Step 1: Detect project
|
|
52
|
+
console.log(bold('🔍 Loading project context...'));
|
|
53
|
+
const project = await detectProject(appPath);
|
|
54
|
+
if (project) {
|
|
55
|
+
console.log(success(` ✓ ${formatRuntime(project)} (${project.sku})`));
|
|
56
|
+
if (project.functions.length > 0) {
|
|
57
|
+
console.log(dim(` Functions: ${project.functions.map(f => `${f.name} (${f.type})`).join(', ')}`));
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
console.log(warning(' ⚠ No Azure Functions project detected. The agent can help you create one.'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Show skill status (informational only — setup runs after agent selection)
|
|
64
|
+
const skillsDir = join(appPath, '.agents', 'skills');
|
|
65
|
+
const needsSetup = !existsSync(skillsDir);
|
|
66
|
+
if (!needsSetup) {
|
|
67
|
+
try {
|
|
68
|
+
const { readdir } = await import('node:fs/promises');
|
|
69
|
+
const skills = (await readdir(skillsDir)).filter(d => !d.startsWith('.'));
|
|
70
|
+
console.log(dim(` Skills: ${skills.length} installed in .agents/skills/`));
|
|
71
|
+
} catch { /* ignore */ }
|
|
72
|
+
}
|
|
73
|
+
console.log();
|
|
74
|
+
|
|
75
|
+
// Step 2: Detect agents and select (only CLI-launchable agents)
|
|
76
|
+
console.log(bold('🤖 Detecting coding agents...'));
|
|
77
|
+
let agents = await detectAgents(appPath);
|
|
78
|
+
const launchableAgents = agents.filter(a => LAUNCHERS[a.id] && a.type === 'cli');
|
|
79
|
+
|
|
80
|
+
let selectedId;
|
|
81
|
+
|
|
82
|
+
if (agentFlag) {
|
|
83
|
+
// Validate explicit agent
|
|
84
|
+
const launcher = LAUNCHERS[agentFlag];
|
|
85
|
+
if (!launcher) {
|
|
86
|
+
console.error(errorColor(` ✗ Unknown agent: ${agentFlag}`));
|
|
87
|
+
console.error(dim(` Available: ${Object.keys(LAUNCHERS).join(', ')}`));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
selectedId = agentFlag;
|
|
91
|
+
} else if (launchableAgents.length === 0) {
|
|
92
|
+
console.log(warning(' ⚠ No supported CLI agents detected.'));
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(' Install one of the following:');
|
|
95
|
+
console.log(dim(' • Claude Code: https://claude.ai/download'));
|
|
96
|
+
console.log(dim(' • GitHub Copilot CLI: https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-in-the-command-line'));
|
|
97
|
+
console.log(dim(' • Codex CLI: npm install -g @openai/codex'));
|
|
98
|
+
console.log();
|
|
99
|
+
console.log(dim(' Or use --agent to specify: fnx chat --agent claude-code'));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
} else {
|
|
102
|
+
for (const a of launchableAgents) {
|
|
103
|
+
console.log(success(` ✓ ${a.name}`));
|
|
104
|
+
}
|
|
105
|
+
console.log();
|
|
106
|
+
|
|
107
|
+
if (launchableAgents.length === 1) {
|
|
108
|
+
selectedId = launchableAgents[0].id;
|
|
109
|
+
} else {
|
|
110
|
+
selectedId = await promptAgentSelection(launchableAgents);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Step 3: Auto-run setup if needed (after agent is selected)
|
|
115
|
+
if (needsSetup) {
|
|
116
|
+
console.log();
|
|
117
|
+
console.log(warning(' ⚠ No skills installed. Running fnx setup for ' + selectedId + '...'));
|
|
118
|
+
console.log();
|
|
119
|
+
const { runSetup } = await import('../setup/index.js');
|
|
120
|
+
await runSetup(['--all', '--agent', selectedId, '--app-path', appPath]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Step 4: Generate .fnx/agent.md
|
|
124
|
+
const agentMdPath = join(appPath, '.fnx', 'agent.md');
|
|
125
|
+
await generateAgentMd(appPath, project, agentMdPath);
|
|
126
|
+
|
|
127
|
+
// Step 5: Launch agent (skip if --setup-only)
|
|
128
|
+
if (setupOnly) {
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(success(' ✓ Setup complete. Skipping agent launch (--setup-only).'));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const launcher = LAUNCHERS[selectedId];
|
|
134
|
+
await launchAgent(selectedId, launcher, appPath, project, promptFlag);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function launchAgent(agentId, launcher, appPath, project, prompt) {
|
|
138
|
+
const agentName = agentId.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
139
|
+
|
|
140
|
+
console.log(bold('🚀 Launching ' + agentName + '...'));
|
|
141
|
+
console.log(dim(` ${launcher.description}`));
|
|
142
|
+
console.log();
|
|
143
|
+
|
|
144
|
+
console.log('┌' + '─'.repeat(50) + '┐');
|
|
145
|
+
console.log('│ ' + bold('fnx chat') + ' • ' + agentName.padEnd(38) + '│');
|
|
146
|
+
if (project) {
|
|
147
|
+
console.log('│ ' + dim(`SKU: ${project.sku} | ${project.functions.length} functions`).padEnd(56) + '│');
|
|
148
|
+
}
|
|
149
|
+
console.log('└' + '─'.repeat(50) + '┘');
|
|
150
|
+
console.log();
|
|
151
|
+
|
|
152
|
+
const args = launcher.buildArgs({ appPath, project });
|
|
153
|
+
if (prompt) args.push(prompt);
|
|
154
|
+
|
|
155
|
+
// Launch the agent as an interactive child process
|
|
156
|
+
// Use shell: false to prevent shell injection via user-controlled args (e.g., --prompt)
|
|
157
|
+
const child = spawn(launcher.command, args, {
|
|
158
|
+
cwd: appPath,
|
|
159
|
+
stdio: 'inherit',
|
|
160
|
+
shell: false,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
child.on('error', (err) => {
|
|
164
|
+
console.error(errorColor(` ✗ Failed to launch ${agentName}: ${err.message}`));
|
|
165
|
+
if (err.code === 'ENOENT') {
|
|
166
|
+
console.error(dim(` Make sure '${launcher.command}' is installed and in your PATH.`));
|
|
167
|
+
}
|
|
168
|
+
process.exit(1);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
child.on('exit', (code) => {
|
|
172
|
+
if (code !== 0 && code !== null) {
|
|
173
|
+
console.log(warning(`\n ${agentName} exited with code ${code}`));
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function generateAgentMd(appPath, project, outputPath) {
|
|
179
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
180
|
+
|
|
181
|
+
const lines = [
|
|
182
|
+
'# Azure Functions Development Agent',
|
|
183
|
+
'',
|
|
184
|
+
'You are assisting a developer building Azure Functions applications with fnx.',
|
|
185
|
+
'',
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
if (project) {
|
|
189
|
+
const funcList = project.functions.map(f => f.name + ' (' + f.type + ')').join(', ') || 'none detected';
|
|
190
|
+
lines.push(
|
|
191
|
+
'## Project Context',
|
|
192
|
+
'- **Runtime:** ' + formatRuntime(project),
|
|
193
|
+
'- **Programming Model:** ' + (project.programmingModel || 'v4'),
|
|
194
|
+
'- **SKU:** ' + project.sku,
|
|
195
|
+
'- **Functions:** ' + funcList,
|
|
196
|
+
'- **Emulator:** fnx (SKU-aware local emulator)',
|
|
197
|
+
);
|
|
198
|
+
} else {
|
|
199
|
+
lines.push(
|
|
200
|
+
'## No Project Detected',
|
|
201
|
+
'No Azure Functions project was found in the current directory.',
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
lines.push(
|
|
206
|
+
'',
|
|
207
|
+
'## Available MCP Tools',
|
|
208
|
+
'If the fnx Templates MCP server is configured, you can use:',
|
|
209
|
+
'- `functions_language_list` — Get supported languages and runtime versions',
|
|
210
|
+
'- `functions_template_get` — Generate function template code',
|
|
211
|
+
'- `functions_project_get` — Scaffold project files',
|
|
212
|
+
'',
|
|
213
|
+
'## Guidelines',
|
|
214
|
+
'- Always use the latest programming model for the detected runtime',
|
|
215
|
+
'- Check SKU compatibility before suggesting triggers/bindings',
|
|
216
|
+
'- Use `fnx start` for local testing (not `func start`)',
|
|
217
|
+
'- Use `app-config.yaml` for non-secret config (committed to git)',
|
|
218
|
+
'- Do NOT put secrets in workspace files',
|
|
219
|
+
'- Refer to installed skills for detailed guidance',
|
|
220
|
+
'',
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
await writeFile(outputPath, lines.join('\n'));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function promptAgentSelection(agents) {
|
|
227
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
228
|
+
console.log('Which agent would you like to use?');
|
|
229
|
+
agents.forEach((a, i) => {
|
|
230
|
+
console.log(` ${i + 1}. ${a.name}${i === 0 ? dim(' (recommended)') : ''}`);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return new Promise((resolve) => {
|
|
234
|
+
rl.question('\nSelect [1]: ', (answer) => {
|
|
235
|
+
rl.close();
|
|
236
|
+
const idx = parseInt(answer || '1', 10) - 1;
|
|
237
|
+
resolve(agents[Math.max(0, Math.min(idx, agents.length - 1))].id);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function formatRuntime(project) {
|
|
243
|
+
if (!project) return 'unknown';
|
|
244
|
+
const name = project.runtime === 'node' ? 'Node.js' : project.runtime;
|
|
245
|
+
return `${name} (${project.language || project.runtime})`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolveAppPath(args) {
|
|
249
|
+
const explicit = getFlag(args, '--app-path');
|
|
250
|
+
return explicit ? resolve(explicit) : process.cwd();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function getFlag(args, name) {
|
|
254
|
+
const idx = args.indexOf(name);
|
|
255
|
+
return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function printChatHelp() {
|
|
259
|
+
console.log(`${title('Usage:')} fnx chat [options]
|
|
260
|
+
|
|
261
|
+
${title('Description:')}
|
|
262
|
+
Launch a coding agent with Azure Functions context. Detects your project,
|
|
263
|
+
generates context files, and starts your preferred coding agent.
|
|
264
|
+
|
|
265
|
+
${title('Options:')}
|
|
266
|
+
${success('--agent')} <name> Use a specific agent: ${funcName('claude-code')}, ${funcName('github-copilot')}, ${funcName('codex')}
|
|
267
|
+
${success('--app-path')} <dir> Path to function app (default: current directory)
|
|
268
|
+
${success('--prompt')} <text> Pass prompt text as CLI argument to the agent
|
|
269
|
+
${success('-h')}, ${success('--help')} Show this help
|
|
270
|
+
|
|
271
|
+
${title('Examples:')}
|
|
272
|
+
${dim('# Auto-detect agent and launch')}
|
|
273
|
+
fnx chat
|
|
274
|
+
|
|
275
|
+
${dim('# Use Claude Code specifically')}
|
|
276
|
+
fnx chat --agent claude-code
|
|
277
|
+
|
|
278
|
+
${dim('# Non-interactive mode')}
|
|
279
|
+
fnx chat --prompt "Add a timer trigger that runs every 5 minutes"
|
|
280
|
+
`);
|
|
281
|
+
}
|
package/lib/cli.js
CHANGED
|
@@ -166,6 +166,28 @@ export async function main(args) {
|
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
if (cmd === 'setup') {
|
|
170
|
+
if (hasHelp(args.slice(1))) {
|
|
171
|
+
const { printSetupHelp } = await import('./setup/index.js');
|
|
172
|
+
printSetupHelp();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const { runSetup } = await import('./setup/index.js');
|
|
176
|
+
await runSetup(args.slice(1));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (cmd === 'chat') {
|
|
181
|
+
if (hasHelp(args.slice(1))) {
|
|
182
|
+
const { printChatHelp } = await import('./chat/index.js');
|
|
183
|
+
printChatHelp();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const { runChat } = await import('./chat/index.js');
|
|
187
|
+
await runChat(args.slice(1));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
if (cmd !== 'start') {
|
|
170
192
|
console.error(errorColor(`Unknown command: ${cmd}\n`));
|
|
171
193
|
printHelp();
|
|
@@ -482,6 +504,8 @@ function printHelp() {
|
|
|
482
504
|
${title('Commands:')}
|
|
483
505
|
${funcName('init')} Initialize a new Azure Functions project.
|
|
484
506
|
${funcName('start')} Launch the Azure Functions host runtime for a specific SKU.
|
|
507
|
+
${funcName('setup')} Add AI agent skills, MCP config, and instructions.
|
|
508
|
+
${funcName('chat')} Launch a coding agent with Azure Functions context.
|
|
485
509
|
${funcName('doctor')} Validate project setup and diagnose common issues.
|
|
486
510
|
${funcName('sync')} Sync cached host/extensions with current catalog profile.
|
|
487
511
|
${funcName('pack')} Package a Functions app into a deployment zip.
|
package/lib/config.js
CHANGED
|
@@ -144,7 +144,7 @@ export async function migrateConfig(appPath) {
|
|
|
144
144
|
*/
|
|
145
145
|
export async function createAppConfig(appPath, overrides = {}, options = {}) {
|
|
146
146
|
const appConfigPath = join(appPath, APP_CONFIG_FILE);
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
// Skip if already exists
|
|
149
149
|
if (await fileExists(appConfigPath)) {
|
|
150
150
|
return false;
|
|
@@ -152,7 +152,7 @@ export async function createAppConfig(appPath, overrides = {}, options = {}) {
|
|
|
152
152
|
|
|
153
153
|
const localSettingsPath = join(appPath, LOCAL_SETTINGS_FILE);
|
|
154
154
|
let localSettings = {};
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
if (await fileExists(localSettingsPath)) {
|
|
157
157
|
try {
|
|
158
158
|
localSettings = await readJsonFile(localSettingsPath);
|
|
@@ -164,11 +164,6 @@ export async function createAppConfig(appPath, overrides = {}, options = {}) {
|
|
|
164
164
|
// Build config using shared function (overrides take precedence)
|
|
165
165
|
const config = buildConfigFromLocalSettings(localSettings, overrides);
|
|
166
166
|
|
|
167
|
-
// Ensure EnableWorkerIndexing is set
|
|
168
|
-
config.configurations = config.configurations || {};
|
|
169
|
-
config.configurations.AzureWebJobsFeatureFlags =
|
|
170
|
-
config.configurations.AzureWebJobsFeatureFlags || 'EnableWorkerIndexing';
|
|
171
|
-
|
|
172
167
|
// Write app-config.yaml
|
|
173
168
|
await writeFile(appConfigPath, generateYaml(config), 'utf-8');
|
|
174
169
|
|
|
@@ -407,7 +402,6 @@ async function interactiveCreate(appPath) {
|
|
|
407
402
|
local: { targetSku: 'flex' },
|
|
408
403
|
runtime: { name: runtime },
|
|
409
404
|
configurations: {
|
|
410
|
-
AzureWebJobsFeatureFlags: 'EnableWorkerIndexing',
|
|
411
405
|
},
|
|
412
406
|
};
|
|
413
407
|
|