Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
22e1127
feat(fiori-mcp-server): promote static functionalities to top-level M…
devinea May 8, 2026
b71dbf9
test(fiori-mcp-server): add unit tests for new top-level tools
devinea May 8, 2026
2c75540
Linting auto fix commit
github-actions[bot] May 8, 2026
0c84470
fix(fiori-mcp-server): address PR review feedback on top-level tool p…
devinea May 17, 2026
305ceac
Merge remote-tracking branch 'origin/main' into refactor_fiori_mcp_tools
devinea May 21, 2026
b49b289
Merge branch 'main' into refactor_fiori_mcp_tools
devinea May 27, 2026
099d224
fix(fiori-mcp-server): resolve merge conflicts and fix build/test fai…
devinea Jun 5, 2026
88c0328
chore(fiori-mcp-server): fix Prettier formatting in new test files
devinea Jun 5, 2026
0aca2ba
refactor(fiori-mcp-server): add .js ESM extensions and broader refact…
devinea Jun 5, 2026
f9ec385
Merge remote-tracking branch 'origin/main' into refactor_fiori_mcp_tools
devinea Jun 5, 2026
fb27f5f
Merge remote-tracking branch 'origin/main' into refactor_fiori_mcp_tools
devinea Jun 8, 2026
3ede6ae
fix(fiori-mcp-server): address PR review and implement #4652
devinea Jun 8, 2026
8ee6455
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 8, 2026
040050c
refactor(fiori-mcp-server): use original functionality names as tool …
devinea Jun 10, 2026
995939a
Fix bundle scripts
IainSAP Jun 11, 2026
fe11c9c
Merge remote-tracking branch 'origin/main' into refactor_fiori_mcp_tools
IainSAP Jun 11, 2026
9ac0dc6
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 11, 2026
3fb9135
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 11, 2026
96e60e9
Remove not needed rename
IainSAP Jun 11, 2026
227e17d
Merge remote-tracking branch 'origin/refactor_fiori_mcp_tools' into r…
IainSAP Jun 11, 2026
cced4d5
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 11, 2026
bb35d05
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 11, 2026
a7df677
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 15, 2026
3e6a966
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 16, 2026
7a528f2
fix: address PR review comments
devinea Jun 16, 2026
92c0d31
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 16, 2026
8bcdac5
Merge remote-tracking branch 'origin/main' into refactor_fiori_mcp_tools
devinea Jun 16, 2026
a985287
Merge remote-tracking branch 'origin/refactor_fiori_mcp_tools' into r…
devinea Jun 16, 2026
cdd6b4a
chore: update changeset to reflect all current changes
devinea Jun 16, 2026
ea93875
refactor: move promoted tool implementations out of functionalities/
devinea Jun 16, 2026
7cc7e6e
refactor: remove dead services/indexer-simple.ts and services/filesto…
devinea Jun 16, 2026
f63d88d
refactor: remove dead code in fiori-mcp-server
devinea Jun 16, 2026
044ce8a
Linting auto fix commit
github-actions[bot] Jun 16, 2026
d1169a1
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 17, 2026
4b8eb07
Adds destination support for generation of apps in BAS
IainSAP Jun 17, 2026
9e26a66
feat: centralize template path resolution via getTemplatePath helper …
815are Jun 17, 2026
9afb4c3
test: update list-functionalities tests after removing CAP from FUNCT…
devinea Jun 17, 2026
8eb9c42
Fixes incorrect entity name wrapping causing preview to fail, adds tests
IainSAP Jun 17, 2026
90eb530
Merge branch 'main' into refactor_fiori_mcp_tools
devinea Jun 17, 2026
13f4d75
Adds dest support - todo: filter dests that may require basic auth
IainSAP Jun 17, 2026
c8b59c0
Merge remote-tracking branch 'origin/refactor_fiori_mcp_tools' into r…
IainSAP Jun 17, 2026
a8c1c5d
Add more dest related tests
IainSAP Jun 17, 2026
f60c3c2
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 17, 2026
b2395bc
Linting auto fix commit
github-actions[bot] Jun 17, 2026
779b921
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 17, 2026
bc04fa5
Remove noAuth and generic dests to align with SC MCP
IainSAP Jun 17, 2026
fe1f6c1
Merge remote-tracking branch 'origin/refactor_fiori_mcp_tools' into r…
IainSAP Jun 17, 2026
5363d39
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 18, 2026
dc0d3e8
Merge branch 'main' into refactor_fiori_mcp_tools
IainSAP Jun 18, 2026
caab304
chore: update changeset to reflect BAS/destination support and entity…
devinea Jun 18, 2026
9b8f238
feat: add adp specific tools
nikmace May 13, 2026
8e9dc6a
feat: add skills
nikmace May 19, 2026
74b2305
feat: update skills
nikmace May 20, 2026
caf9e7c
Introduce frontend action calls (#4704)
StefanBlamberg May 20, 2026
c4d0774
refactor: move frontend actions to tool folder
nikmace May 20, 2026
c7508c6
feat: format tool description
nikmace May 20, 2026
f70de04
fix: start playwright browser window max size
nikmace May 20, 2026
ef4ed31
refactor: improve code
nikmace May 21, 2026
01a133a
refactor: change folder and file names
nikmace May 21, 2026
107b7f8
refactor: improve code
nikmace May 21, 2026
5443811
refactor: improve code
nikmace May 21, 2026
4d572ac
refactor: jsdocs
nikmace May 21, 2026
3646387
refactor: move switch check
nikmace May 21, 2026
083651c
refactor: improve controller extension tool
nikmace May 21, 2026
2f04966
feat: install chromium if not installed, change skill name
nikmace May 26, 2026
ef3b120
feat: update skill
nikmace May 26, 2026
f6b9cb2
chore: update pnpm-lock.yaml after rebase onto refactor_fiori_mcp_tools
nikmace Jun 19, 2026
f099228
chore: update cset
nikmace Jun 19, 2026
03ed6c4
fix: migrate new ADP/RTA tool files to ESM imports and fix test suite
nikmace Jun 19, 2026
60f6f3e
refactor: change text
nikmace Jun 19, 2026
28029cf
fix(fiori-mcp-server): restore working playwright-bridge with newCont…
nikmace Jun 22, 2026
3a2469d
Merge remote-tracking branch 'origin/main' into feat/unified-flex-flo…
nikmace Jun 29, 2026
198f906
Linting auto fix commit
github-actions[bot] Jun 29, 2026
20e4d63
Merge remote-tracking branch 'origin/main' into feat/unified-flex-flo…
nikmace Jul 21, 2026
e3eef67
fix(fiori-mcp-server): strip credentials from response, surface spawn…
nikmace Jul 21, 2026
573a8ff
fix(fiori-mcp-server): close BrowserContext after page close to preve…
nikmace Jul 21, 2026
398876b
Linting auto fix commit
github-actions[bot] Jul 21, 2026
69733d6
fix(fiori-mcp-server): rename adp skill to sap-fiori-* prefix, add au…
nikmace Jul 21, 2026
b9e553b
fix(fiori-mcp-server): make sap-fiori-adp-controller-extension-flow s…
nikmace Jul 21, 2026
6aaf4d3
fix(fiori-mcp-server): rename adp-project-setup skill to sap-fiori-* …
nikmace Jul 21, 2026
861358e
fix(fiori-mcp-server): fix parser fence matching, warn on unclosed bl…
nikmace Jul 21, 2026
ad0f73f
Linting auto fix commit
github-actions[bot] Jul 21, 2026
f81daf5
test(fiori-mcp-server): add tests for generate-adaptation-project, op…
nikmace Jul 21, 2026
2157272
Merge branch 'feat/unified-flex-flow-for-adp-mcp' of https://github.c…
nikmace Jul 21, 2026
0be5bae
Linting auto fix commit
github-actions[bot] Jul 21, 2026
9561342
fix(fiori-mcp-server): use platform-agnostic path check in generate-a…
nikmace Jul 22, 2026
1c78410
Merge branch 'feat/unified-flex-flow-for-adp-mcp' of https://github.c…
nikmace Jul 22, 2026
f59fd6d
test(fiori-mcp-server): improve coverage for server, run-rta-workflow…
nikmace Jul 22, 2026
1f7f2c0
fix(fiori-mcp-server): reduce parser cognitive complexity and use Reg…
nikmace Jul 22, 2026
93d0c14
Linting auto fix commit
github-actions[bot] Jul 22, 2026
1bf241f
Merge branch 'feat/unified-flex-flow-for-adp-mcp' of https://github.c…
nikmace Jul 22, 2026
2862f56
fix(fiori-mcp-server): use replaceAll instead of replace with global …
nikmace Jul 22, 2026
21f8169
fix(fiori-mcp-server): reduce getPortFromPid cognitive complexity by …
nikmace Jul 22, 2026
4008336
fix(fiori-mcp-server): use Number.parseInt and Number.isNaN instead o…
nikmace Jul 22, 2026
7cef784
fix(fiori-mcp-server): reduce openAdaptationEditor cognitive complexi…
nikmace Jul 22, 2026
00b1397
fix(fiori-mcp-server): move constants and interface to top of open-ad…
nikmace Jul 22, 2026
57b3b08
Linting auto fix commit
github-actions[bot] Jul 22, 2026
7ad24b1
test(fiori-mcp-server): add missing coverage for server CallTool hand…
nikmace Jul 22, 2026
782c682
Merge branch 'feat/unified-flex-flow-for-adp-mcp' of https://github.c…
nikmace Jul 22, 2026
93be6c6
test(fiori-mcp-server): improve coverage for optional fields, null va…
nikmace Jul 22, 2026
0e0a7ee
test(fiori-mcp-server): add port-detection tests by mocking execAsync…
nikmace Jul 22, 2026
efafe89
test(fiori-mcp-server): extend coverage for port detection, optional …
nikmace Jul 22, 2026
f1e6cae
Linting auto fix commit
github-actions[bot] Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fiori-mcp-server-adp-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sap-ux/fiori-mcp-server": minor
---

FEAT: add ADP top-level tools (generate_adaptation_project, open_adaptation_editor, adp_controller_extension)
3 changes: 2 additions & 1 deletion packages/fiori-mcp-server/jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default {
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
modulePathIgnorePatterns: [...baseConfig.modulePathIgnorePatterns, '<rootDir>/test/data/'],
transformIgnorePatterns: [
'node_modules/(?!(@huggingface|@sap-ux|@sap-ux-private|@sap/ux-specification|@sap/ux-cds-compiler-facade)/)'
'node_modules/(?!(@huggingface|@sap-ux|@sap-ux-private|@sap/ux-specification|@sap/ux-cds-compiler-facade|@sap-devx)/)'
],
// Routes @sap/ux-cds-compiler-facade to .mjs ESM mock
resolver: '<rootDir>/jest.resolver.cjs',
Expand All @@ -24,6 +24,7 @@ export default {
// Stub fe-fpm-writer to prevent the CJS require chain: ux-specification → fe-fpm-writer →
// fiori-annotation-api → @sap/ux-cds-compiler-facade which conflicts with the ESM mock
'^@sap-ux/fe-fpm-writer$': '<rootDir>/test/__mocks__/@sap-ux/fe-fpm-writer.cjs',
'^@sap-ux/adp-tooling$': '<rootDir>/test/__mocks__/@sap-ux/adp-tooling.cjs',
'^@sap/ux-specification$': '<rootDir>/test/__mocks__/@sap/ux-specification.mjs',
...baseConfig.moduleNameMapper,
'^@huggingface/transformers$': '<rootDir>/test/__mocks__/@huggingface/transformers.cjs'
Expand Down
8 changes: 6 additions & 2 deletions packages/fiori-mcp-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
"license": "Apache-2.0",
"main": "dist/index.js",
"scripts": {
"build-esbuild-base": "esbuild --bundle --platform=node --target=node20 --outdir=dist --external:vscode --external:@lancedb/lancedb --external:@xenova/transformers --external:@sap-ux/fiori-docs-embeddings --external:@sap-ux/store --external:playwright-core --main-fields=module,main src/index.ts",
"build-bundle": "node scripts/bundle.mjs",
"build-dev": "NODE_ENV=development node scripts/bundle.mjs",
"build-dev": "pnpm run build-esbuild-base --sourcemap=inline",
"build-compile": "tsc --noEmit",
"build": "npm-run-all build-compile build-bundle",
"watch": "tsc --watch",
Expand All @@ -47,7 +48,8 @@
"dist",
"!dist/**/*.map",
"assets/icon.svg",
"assets/icon.png"
"assets/icon.png",
"skills"
],
"bin": {
"fiori-mcp": "./dist/index.js"
Expand Down Expand Up @@ -82,13 +84,15 @@
"@types/json-schema": "7.0.15",
"@types/mem-fs": "1.1.2",
"@types/mem-fs-editor": "7.0.1",
"@sap-ux/adp-tooling": "workspace:*",
"cross-env": "10.1.0",
"esbuild": "0.27.4",
"i18next": "25.10.10",
"mem-fs": "2.1.0",
"mem-fs-editor": "9.4.0",
"npm-run-all2": "8.0.4",
"os-name": "4.0.1",
"playwright-core": "1.58.2",
"promptfoo": "0.121.15",
"xml-formatter": "3.7.0",
"ts-node": "10.9.2",
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
name: sap-fiori-adp-project-setup
author: sap-fiori-tools
version: 0.0.1
description: Use when creating a new SAP Fiori adaptation project, setting up an adaptation editor, or adapting an existing Fiori app on an SAP system
---

# ADP Project Setup

End-to-end workflow for creating a SAP Fiori adaptation project and launching the adaptation editor.

## Prerequisites

- fiori-mcp server connected (provides `mcp__fiori-mcp__*` tools)
- User knows which SAP system and application to adapt (or will be guided)

### Installing fiori-mcp

This skill requires the `@sap-ux/fiori-mcp-server` MCP server. It is part of the [SAP UX Tools](https://github.com/SAP/open-ux-tools) open-source project.

**Install via npm:**

```bash
npm install -g @sap-ux/fiori-mcp-server
```

**Configure in your MCP client** (e.g. Claude Desktop `claude_desktop_config.json`, or VS Code `settings.json`):

```json
{
"mcpServers": {
"fiori-mcp": {
"command": "npx",
"args": ["-y", "@sap-ux/fiori-mcp-server"]
}
}
}
```

After adding the server, restart your MCP client. The `mcp__fiori-mcp__*` tools should appear in the available tools list.

## Workflow

### Step 1: Discover Available Systems

Call `mcp__fiori-mcp__list_sap_systems` to retrieve the list of configured SAP systems.

Present the systems to the user. If no systems are found, inform the user they need to configure a system first (via SAP Fiori tools system store or BAS destinations).

### Step 2: Gather Parameters

Ask the user for:
1. **System** — which SAP system to use (from the list)
2. **Application** — the application ID to adapt (e.g., `sap.ui.demoapps.rta.fe`)
3. **Target folder** — where to create the project (default: current working directory)
4. **Project name** — optional, defaults to `app.variant`

If the user does not know the application ID, suggest they check the Fiori Apps Library or their system's app index.

### Step 3: Generate the Adaptation Project

Call `mcp__fiori-mcp__generate_adaptation_project` with:
- `system`: selected system name
- `application`: the application ID
- `appPath`: current working directory or user-specified path
- `targetFolder`: where to generate (absolute path)
- `projectName`: user's choice or default

Optional parameters if user provides them: `namespace`, `applicationTitle`, `client`, `username`, `password`.

Wait for completion. Report success or failure.

### Step 4: Patch ui5.yaml for Local Proxy (DEMO ONLY)

**Only if the user explicitly requests demo/local mode or mentions localhost proxy.**

Read the generated `ui5.yaml` in the project root. In the `fiori-tools-proxy` middleware config, change:
- `url` → `http://localhost:8080`
- `version` → leave empty (remove the value)
- Keep `path` as-is

### Step 5: Launch the Adaptation Editor

Call `mcp__fiori-mcp__open_adaptation_editor` with:
- `appPath`: absolute path to the generated adaptation project root (where `package.json` resides)

The tool returns:
- `editorUrl` — full URL to open the adaptation editor
- `processId` — background server PID
- `port` — server port

### Step 6: Report Success

Present to the user:
- Project location (absolute path)
- Editor URL
- Server PID and port (for cleanup)
- Next step: "Use the `sap-fiori-adp-controller-extension-flow` skill to make RTA changes, or open the URL in your browser."

## Error Handling

- **No systems found:** User must configure systems first
- **Generation fails:** Check credentials, application ID validity, system connectivity
- **Editor launch fails:** Verify project path is correct and `package.json` exists
- **ui5.yaml not found:** Skip patch, warn user

## Cleanup

The editor server runs in background. To stop it later:
```bash
kill <processId>
# or by port:
lsof -ti:<port> | xargs kill
```
3 changes: 3 additions & 0 deletions packages/fiori-mcp-server/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ export const LATEST_UI5_VERSION = '1.136.7';
export const ADD_PAGE = 'add-page';
export const DELETE_PAGE = 'delete-page';
export const CREATE_CONTROLLER_EXTENSION_FUNCTIONALITY_ID = 'create-controller-extension';
export const GENERATE_ADAPTATION_PROJECT_ID = 'generate-adaptation-project';
export const ADP_CONTROLLER_EXTENSION_FUNCTIONALITY_ID = 'adp-controller-extension';
export const OPEN_ADAPTATION_EDITOR_ID = 'open-adaptation-editor';
export const EXTENSION_FILE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;

// Telemetry event names
Expand Down
30 changes: 28 additions & 2 deletions packages/fiori-mcp-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ import {
downloadODataServiceMetadata,
generateFioriAppOData,
generateFioriAppCap,
generateAdaptationProject,
openAdaptationEditor,
adpControllerExtension,
runRtaWorkflowStep,
tools
} from './tools/index.js';
import { stopBrowser } from './tools/run-rta-workflow-step/browser/index.js';
import { TelemetryHelper, unknownTool, type TelemetryData } from './telemetry/index.js';
import { TELEMETRY_MCP_SERVER_INITIALIZED, TELEMETRY_MCP_LIST_TOOLS } from './constant.js';
import type {
Expand All @@ -30,7 +35,11 @@ import type {
DocSearchInput,
ListFioriAppsInput,
ListFunctionalitiesInput,
DownloadODataServiceMetadataInput
DownloadODataServiceMetadataInput,
GenerateAdaptationProjectInput,
OpenAdaptationEditorInput,
AdpControllerExtensionInput,
RunRtaWorkflowStepInput
} from './types/index.js';
import type { GeneratorConfigOData, GeneratorConfigCAP } from './tools/schemas/index.js';
import { logger } from './utils/logger.js';
Expand All @@ -44,6 +53,10 @@ type ToolArgs =
| DownloadODataServiceMetadataInput
| GeneratorConfigOData
| GeneratorConfigCAP
| GenerateAdaptationProjectInput
| OpenAdaptationEditorInput
| AdpControllerExtensionInput
| RunRtaWorkflowStepInput
| Record<string, unknown>;

const FALLBACK_PROTOCOL_VERSION = '2024-11-05';
Expand Down Expand Up @@ -107,6 +120,7 @@ export class FioriFunctionalityServer {
private setupErrorHandling(): void {
this.server.onerror = (error): void => logger.error(`[MCP Error] ${error}`);
process.on('SIGINT', async () => {
await stopBrowser();
await this.server.close();
process.exit(0);
});
Expand Down Expand Up @@ -273,6 +287,18 @@ Never skip steps or guess functionalityIds. Never use a functionalityId as a too
case 'generate_fiori_app_cap':
result = await generateFioriAppCap(args as GeneratorConfigCAP);
break;
case 'generate_adaptation_project':
result = await generateAdaptationProject(args as GenerateAdaptationProjectInput);
break;
case 'open_adaptation_editor':
result = await openAdaptationEditor(args as OpenAdaptationEditorInput);
break;
case 'adp_controller_extension':
result = await adpControllerExtension(args as AdpControllerExtensionInput);
break;
case 'run_rta_workflow_step':
result = await runRtaWorkflowStep(args as RunRtaWorkflowStepInput);
break;
case 'list_functionality':
result = await listFunctionalities(args as ListFunctionalitiesInput);
break;
Expand All @@ -286,7 +312,7 @@ Never skip steps or guess functionalityIds. Never use a functionalityId as a too
// Do not pass telemetryProperties to unknownTool
await TelemetryHelper.sendTelemetry(unknownTool, {}, (args as any)?.appPath);
throw new Error(
`Unknown tool: ${name}. Try one of: search_docs, list_fiori_apps, list_sap_systems, download_odata_service_metadata, generate_fiori_app_odata, generate_fiori_app_cap, list_functionality, get_functionality_details, execute_functionality.`
`Unknown tool: ${name}. Try one of: search_docs, list_fiori_apps, list_sap_systems, download_odata_service_metadata, generate_fiori_app_odata, generate_fiori_app_cap, generate_adaptation_project, open_adaptation_editor, adp_controller_extension, run_rta_workflow_step, list_functionality, get_functionality_details, execute_functionality.`
);
}
await TelemetryHelper.sendTelemetry(name, telemetryProperties, (args as any)?.appPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { extractFilesFromResponse, isChangeFile } from './parser.js';
export { processAiResponse } from './processor.js';
export { PathTraversalError, resolveWithinAppPath, writeExtractedFile } from './writer.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { ExtractedFile } from '../types.js';
import { logger } from '../../../utils/logger.js';

const PATH_MARKER = /\*\*Path:\*\*\s*(.+)/;
const FENCE_OPEN = /^```(\w+)?/;
const FENCE_CLOSE = /^```\s*$/;

function processOutsideBlock(
line: string,
state: { inCodeBlock: boolean; currentPath: string; currentCode: string }
): void {
const pathMatch = PATH_MARKER.exec(line);
if (pathMatch) {
state.currentPath = pathMatch[1].trim();
return;
}
if (FENCE_OPEN.test(line)) {
state.inCodeBlock = true;
state.currentCode = '';
}
}

function processInsideBlock(
line: string,
state: { inCodeBlock: boolean; currentPath: string; currentCode: string },
codeBlocks: ExtractedFile[]
): void {
if (FENCE_CLOSE.test(line)) {
state.inCodeBlock = false;
if (state.currentPath && state.currentCode.trim()) {
codeBlocks.push({ path: state.currentPath, code: state.currentCode.trim() });
}
state.currentPath = '';
state.currentCode = '';
return;
}
state.currentCode += line + '\n';
}

/**
* Parses an AI response containing markdown code blocks preceded by
* `**Path:** <fullFilePath>` markers and returns the extracted files. Lines
* outside fenced blocks are ignored. A code block without a preceding path
* marker is dropped.
*
* @param content Raw AI response text.
* @returns Files declared in the response.
*/
export function extractFilesFromResponse(content: string): ExtractedFile[] {
const codeBlocks: ExtractedFile[] = [];
const state = { inCodeBlock: false, currentPath: '', currentCode: '' };

for (const line of content.split('\n')) {
if (!state.inCodeBlock) {
processOutsideBlock(line, state);
} else {
processInsideBlock(line, state, codeBlocks);
}
}

if (state.inCodeBlock) {
logger.warn(
`AI response ended with an unclosed code block — content discarded for path: "${state.currentPath || '(no path)'}"`
);
}

return codeBlocks;
Comment thread
nikmace marked this conversation as resolved.
}

/**
* Returns `true` for `.change` files. The AI may emit those alongside code
* files but they are persisted by a separate flow, so the writer skips them.
*
* @param filePath Candidate file path or filename.
* @returns Whether the path looks like a flexibility change file.
*/
export function isChangeFile(filePath: string): boolean {
return filePath.toLowerCase().endsWith('.change');
}
Loading
Loading