sofia-cli 0.1.2 → 0.1.4
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 +42 -20
- package/dist/infra/deploy.sh +193 -0
- package/dist/infra/gather-env.sh +211 -0
- package/dist/infra/infra/deploy.sh +193 -0
- package/dist/infra/infra/gather-env.sh +211 -0
- package/dist/infra/infra/main.bicep +90 -0
- package/dist/infra/infra/main.bicepparam +18 -0
- package/dist/infra/infra/resources.bicep +134 -0
- package/dist/infra/infra/teardown.sh +114 -0
- package/dist/infra/main.bicep +90 -0
- package/dist/infra/main.bicepparam +18 -0
- package/dist/infra/resources.bicep +134 -0
- package/dist/infra/teardown.sh +114 -0
- package/dist/src/cli/developCommand.js +0 -2
- package/dist/src/cli/index.js +8 -1
- package/dist/src/cli/workshopCommand.js +1 -1
- package/dist/src/develop/index.js +1 -1
- package/dist/src/develop/pocUtils.js +228 -0
- package/dist/src/develop/ralphLoop.js +8 -27
- package/dist/src/shared/data/cards.json +655 -670
- package/docs/architecture.md +2 -1
- package/package.json +5 -3
- package/src/cli/developCommand.ts +1 -3
- package/src/cli/index.ts +11 -1
- package/src/cli/workshopCommand.ts +21 -17
- package/src/develop/dynamicScaffolder.ts +36 -30
- package/src/develop/index.ts +13 -2
- package/src/develop/pocUtils.ts +296 -0
- package/src/develop/ralphLoop.ts +8 -28
- package/src/develop/templateRegistry.ts +19 -18
- package/src/shared/data/cards.json +655 -670
- package/tests/e2e/developE2e.spec.ts +3 -61
- package/tests/e2e/developFailureE2e.spec.ts +34 -38
- package/tests/integration/pocGithubMcp.spec.ts +29 -39
- package/tests/integration/pocLocalFallback.spec.ts +29 -39
- package/tests/integration/ralphLoopFlow.spec.ts +46 -66
- package/tests/integration/ralphLoopPartial.spec.ts +30 -37
- package/tests/unit/develop/githubMcpAdapter.spec.ts +0 -134
- package/tests/unit/develop/outputValidator.spec.ts +45 -21
- package/tests/unit/develop/ralphLoop.spec.ts +58 -94
- package/tsconfig.json +2 -1
- package/vitest.workspace.ts +5 -0
- package/dist/src/develop/pocScaffolder.js +0 -542
- package/dist/tests/e2e/developE2e.spec.js +0 -126
- package/dist/tests/e2e/developFailureE2e.spec.js +0 -247
- package/dist/tests/e2e/developPty.spec.js +0 -75
- package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +0 -84
- package/dist/tests/e2e/harness.spec.js +0 -83
- package/dist/tests/e2e/mcpLive.spec.js +0 -120
- package/dist/tests/e2e/newSession.e2e.spec.js +0 -177
- package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +0 -62
- package/dist/tests/e2e/workiqEnrichment.spec.js +0 -56
- package/dist/tests/e2e/zavaSimulation.spec.js +0 -452
- package/dist/tests/fixtures/test-fixture-project/src/add.js +0 -3
- package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +0 -6
- package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +0 -8
- package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +0 -10
- package/dist/tests/fixtures/test-fixture-project/vitest.config.js +0 -6
- package/dist/tests/integration/autoStartConversation.spec.js +0 -138
- package/dist/tests/integration/defaultCommand.spec.js +0 -147
- package/dist/tests/integration/directCommandNonTty.spec.js +0 -224
- package/dist/tests/integration/directCommandTty.spec.js +0 -151
- package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +0 -175
- package/dist/tests/integration/exportArtifacts.spec.js +0 -202
- package/dist/tests/integration/exportFallbackFlow.spec.js +0 -99
- package/dist/tests/integration/mcpDegradationFlow.spec.js +0 -190
- package/dist/tests/integration/mcpTransportFlow.spec.js +0 -139
- package/dist/tests/integration/newSessionFlow.spec.js +0 -343
- package/dist/tests/integration/pocGithubMcp.spec.js +0 -186
- package/dist/tests/integration/pocLocalFallback.spec.js +0 -171
- package/dist/tests/integration/pocScaffold.spec.js +0 -163
- package/dist/tests/integration/ralphLoopFlow.spec.js +0 -359
- package/dist/tests/integration/ralphLoopPartial.spec.js +0 -368
- package/dist/tests/integration/resumeAndBacktrack.spec.js +0 -247
- package/dist/tests/integration/spinnerLifecycle.spec.js +0 -220
- package/dist/tests/integration/summarizationFlow.spec.js +0 -115
- package/dist/tests/integration/testRunnerReal.spec.js +0 -52
- package/dist/tests/integration/webSearchAgent.spec.js +0 -128
- package/dist/tests/live/copilotSdkLive.spec.js +0 -107
- package/dist/tests/live/zavaFullWorkshop.spec.js +0 -392
- package/dist/tests/setup/loadEnv.js +0 -3
- package/dist/tests/unit/cli/developCommand.spec.js +0 -567
- package/dist/tests/unit/cli/directCommands.spec.js +0 -279
- package/dist/tests/unit/cli/envLoader.spec.js +0 -58
- package/dist/tests/unit/cli/ioContext.spec.js +0 -119
- package/dist/tests/unit/cli/preflight.spec.js +0 -108
- package/dist/tests/unit/cli/statusCommand.spec.js +0 -111
- package/dist/tests/unit/cli/workshopClientFallback.spec.js +0 -80
- package/dist/tests/unit/cli/workshopCommand.spec.js +0 -328
- package/dist/tests/unit/config/vitestEnvSetup.spec.js +0 -13
- package/dist/tests/unit/develop/checkpointState.spec.js +0 -315
- package/dist/tests/unit/develop/codeGenerator.spec.js +0 -355
- package/dist/tests/unit/develop/githubMcpAdapter.spec.js +0 -231
- package/dist/tests/unit/develop/mcpContextEnricher.spec.js +0 -433
- package/dist/tests/unit/develop/outputValidator.spec.js +0 -119
- package/dist/tests/unit/develop/pocScaffolder.spec.js +0 -353
- package/dist/tests/unit/develop/ralphLoop.spec.js +0 -1248
- package/dist/tests/unit/develop/templateRegistry.spec.js +0 -85
- package/dist/tests/unit/develop/testRunner.spec.js +0 -249
- package/dist/tests/unit/infraBicep.spec.js +0 -92
- package/dist/tests/unit/infraDeploy.spec.js +0 -82
- package/dist/tests/unit/infraTeardown.spec.js +0 -63
- package/dist/tests/unit/logging/logger.spec.js +0 -43
- package/dist/tests/unit/loop/conversationLoop.spec.js +0 -592
- package/dist/tests/unit/loop/phaseSummarizer.spec.js +0 -141
- package/dist/tests/unit/loop/streamingMarkdown.spec.js +0 -147
- package/dist/tests/unit/mcp/mcpManager.spec.js +0 -279
- package/dist/tests/unit/mcp/mcpTransport.spec.js +0 -529
- package/dist/tests/unit/mcp/retryPolicy.spec.js +0 -218
- package/dist/tests/unit/mcp/timeoutValidation.spec.js +0 -46
- package/dist/tests/unit/mcp/webSearch.spec.js +0 -567
- package/dist/tests/unit/phases/contextSummarizer.spec.js +0 -140
- package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +0 -93
- package/dist/tests/unit/phases/discoveryEnricher.spec.js +0 -411
- package/dist/tests/unit/phases/phaseExtractors.spec.js +0 -352
- package/dist/tests/unit/phases/phaseHandlers.spec.js +0 -425
- package/dist/tests/unit/prompts/promptLoader.spec.js +0 -118
- package/dist/tests/unit/schemas/pocSchemas.spec.js +0 -412
- package/dist/tests/unit/schemas/session.spec.js +0 -257
- package/dist/tests/unit/sessions/exportPaths.spec.js +0 -31
- package/dist/tests/unit/sessions/exportWriter.spec.js +0 -655
- package/dist/tests/unit/sessions/sessionManager.spec.js +0 -151
- package/dist/tests/unit/sessions/sessionStore.spec.js +0 -116
- package/dist/tests/unit/shared/activitySpinner.spec.js +0 -175
- package/dist/tests/unit/shared/cardsLoader.spec.js +0 -76
- package/dist/tests/unit/shared/copilotClient.spec.js +0 -155
- package/dist/tests/unit/shared/errorClassifier.spec.js +0 -131
- package/dist/tests/unit/shared/events.spec.js +0 -55
- package/dist/tests/unit/shared/markdownRenderer.spec.js +0 -35
- package/dist/tests/unit/shared/markdownRendererChunks.spec.js +0 -70
- package/dist/tests/unit/shared/tableRenderer.spec.js +0 -34
- package/dist/vitest.config.js +0 -14
- package/dist/vitest.live.config.js +0 -18
- package/src/develop/pocScaffolder.ts +0 -646
- package/tests/integration/pocScaffold.spec.ts +0 -220
- package/tests/unit/develop/pocScaffolder.spec.ts +0 -451
package/docs/architecture.md
CHANGED
|
@@ -16,7 +16,8 @@ src/
|
|
|
16
16
|
├── develop/ # PoC generation & Ralph loop
|
|
17
17
|
│ ├── index.ts # Barrel export
|
|
18
18
|
│ ├── ralphLoop.ts # Autonomous iteration orchestrator
|
|
19
|
-
│ ├──
|
|
19
|
+
│ ├── pocUtils.ts # PoC output validation & scaffold utilities
|
|
20
|
+
│ ├── dynamicScaffolder.ts # LLM-driven dynamic PoC scaffolding
|
|
20
21
|
│ ├── codeGenerator.ts # LLM-driven code generation & prompt building
|
|
21
22
|
│ ├── testRunner.ts # Test execution and result parsing
|
|
22
23
|
│ ├── mcpContextEnricher.ts# MCP-driven context enrichment (Context7, Azure, web search)
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sofia-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/cli/index.js",
|
|
7
|
+
"license": "MIT",
|
|
7
8
|
"bin": {
|
|
8
9
|
"sofia": "dist/src/cli/index.js"
|
|
9
10
|
},
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsc -p tsconfig.json && npm run build:assets",
|
|
15
|
-
"build:assets": "cp -r src/prompts/ dist/src/ && cp src/shared/data/cards.json dist/src/shared/data/",
|
|
16
|
+
"build:assets": "cp -r src/prompts/ dist/src/ && cp src/shared/data/cards.json dist/src/shared/data/ && cp -r infra/ dist/infra/ ",
|
|
16
17
|
"dev": "tsx watch src/cli/index.ts",
|
|
17
18
|
"start": "tsx src/cli/index.ts",
|
|
18
19
|
"clean": "rimraf dist coverage .nyc_output",
|
|
@@ -27,7 +28,8 @@
|
|
|
27
28
|
"test:e2e": "vitest run tests/e2e",
|
|
28
29
|
"test:live": "vitest run --config vitest.live.config.ts",
|
|
29
30
|
"sofiacli": "node --enable-source-maps dist/cli/index.js",
|
|
30
|
-
"sofia": "npm run sofiacli"
|
|
31
|
+
"sofia": "npm run sofiacli",
|
|
32
|
+
"prepublishOnly": "npm run build && npm link"
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
35
|
"@azure/ai-projects": "^2.0.0-beta.5",
|
|
@@ -20,7 +20,6 @@ import { RalphLoop } from '../develop/ralphLoop.js';
|
|
|
20
20
|
import { McpContextEnricher } from '../develop/mcpContextEnricher.js';
|
|
21
21
|
import { deriveCheckpointState } from '../develop/checkpointState.js';
|
|
22
22
|
import { createDefaultRegistry, selectTemplate } from '../develop/templateRegistry.js';
|
|
23
|
-
import { PocScaffolder } from '../develop/pocScaffolder.js';
|
|
24
23
|
|
|
25
24
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
26
25
|
|
|
@@ -221,7 +220,6 @@ export async function developCommand(
|
|
|
221
220
|
outputDir,
|
|
222
221
|
enricher,
|
|
223
222
|
checkpoint,
|
|
224
|
-
scaffolder: new PocScaffolder(template),
|
|
225
223
|
templateEntry: template,
|
|
226
224
|
onSessionUpdate: async (updated) => {
|
|
227
225
|
await store.save(updated);
|
|
@@ -293,7 +291,7 @@ export async function developCommand(
|
|
|
293
291
|
'Recovery options:',
|
|
294
292
|
` • Resume: sofia dev --session ${sessionId}`,
|
|
295
293
|
` • More iterations: sofia dev --session ${sessionId} --max-iterations 20`,
|
|
296
|
-
|
|
294
|
+
` • More iterations: sofia dev --session ${sessionId} --max-iterations 30`,
|
|
297
295
|
` • Start fresh: sofia dev --session ${sessionId} --force`,
|
|
298
296
|
'',
|
|
299
297
|
].join('\n'),
|
package/src/cli/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - status: display session status
|
|
9
9
|
* - export: export session artifacts
|
|
10
10
|
*/
|
|
11
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
12
|
import path from 'node:path';
|
|
12
13
|
import { fileURLToPath } from 'node:url';
|
|
13
14
|
import { Command } from 'commander';
|
|
@@ -19,6 +20,15 @@ import type { PhaseValue } from '../shared/schemas/session.js';
|
|
|
19
20
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
21
|
loadEnvFile(path.resolve(__dirname, '../../.env'));
|
|
21
22
|
|
|
23
|
+
// ── Read version from package.json (works from both src/ and dist/) ────────
|
|
24
|
+
let _pkgRoot = __dirname;
|
|
25
|
+
while (_pkgRoot !== path.dirname(_pkgRoot) && !existsSync(path.join(_pkgRoot, 'package.json'))) {
|
|
26
|
+
_pkgRoot = path.dirname(_pkgRoot);
|
|
27
|
+
}
|
|
28
|
+
const { version } = JSON.parse(readFileSync(path.join(_pkgRoot, 'package.json'), 'utf-8')) as {
|
|
29
|
+
version: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
22
32
|
// ── Handler types ──────────────────────────────────────────────────────────
|
|
23
33
|
|
|
24
34
|
export interface CliHandlers {
|
|
@@ -36,7 +46,7 @@ export interface CliHandlers {
|
|
|
36
46
|
export function buildCli(handlers?: Partial<CliHandlers>): Command {
|
|
37
47
|
const program = new Command();
|
|
38
48
|
|
|
39
|
-
program.name('sofia').description('sofIA — AI Discovery Workshop CLI').version(
|
|
49
|
+
program.name('sofia').description('sofIA — AI Discovery Workshop CLI').version(version);
|
|
40
50
|
|
|
41
51
|
// ── Global options ──────────────────────────────────────────────────────
|
|
42
52
|
|
|
@@ -19,7 +19,11 @@ import { createPhaseHandler, getPhaseOrder, getNextPhase } from '../phases/phase
|
|
|
19
19
|
import type { PhaseHandlerConfig } from '../phases/phaseHandlers.js';
|
|
20
20
|
import type { SofiaEvent } from '../shared/events.js';
|
|
21
21
|
import { renderMarkdown } from '../shared/markdownRenderer.js';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
destroyWebSearchSession,
|
|
24
|
+
isWebSearchConfigured,
|
|
25
|
+
createWebSearchTool,
|
|
26
|
+
} from '../mcp/webSearch.js';
|
|
23
27
|
import type { WebSearchConfig } from '../mcp/webSearch.js';
|
|
24
28
|
import { loadEnvFile } from './envLoader.js';
|
|
25
29
|
import { loadMcpConfig, McpManager } from '../mcp/mcpManager.js';
|
|
@@ -211,7 +215,7 @@ async function runWorkshop(
|
|
|
211
215
|
session.plan?.architectureNotes,
|
|
212
216
|
session.plan?.dependencies,
|
|
213
217
|
);
|
|
214
|
-
|
|
218
|
+
|
|
215
219
|
const ralphLoop = new RalphLoop({
|
|
216
220
|
client,
|
|
217
221
|
io,
|
|
@@ -232,18 +236,18 @@ async function runWorkshop(
|
|
|
232
236
|
checkpoint,
|
|
233
237
|
templateEntry,
|
|
234
238
|
});
|
|
235
|
-
|
|
239
|
+
|
|
236
240
|
const result = await ralphLoop.run();
|
|
237
241
|
session = result.session;
|
|
238
|
-
|
|
242
|
+
|
|
239
243
|
if (result.finalStatus === 'success') {
|
|
240
244
|
io.write(
|
|
241
245
|
renderMarkdown(
|
|
242
246
|
`\n✅ PoC generated successfully in ${result.outputDir}\n\n` +
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
`Next steps:\n` +
|
|
248
|
+
`- Review the generated code\n` +
|
|
249
|
+
`- Run tests: \`cd ${result.outputDir} && npm test\`\n` +
|
|
250
|
+
`- Continue developing: \`sofia dev --session ${session.sessionId}\`\n`,
|
|
247
251
|
{ isTTY: io.isTTY },
|
|
248
252
|
),
|
|
249
253
|
);
|
|
@@ -251,10 +255,10 @@ async function runWorkshop(
|
|
|
251
255
|
io.write(
|
|
252
256
|
renderMarkdown(
|
|
253
257
|
`\n⚠️ PoC generation incomplete (${result.terminationReason})\n\n` +
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
`The workshop can continue. To retry or resume PoC development:\n` +
|
|
259
|
+
`\`\`\`\n` +
|
|
260
|
+
`sofia dev --session ${session.sessionId}\n` +
|
|
261
|
+
`\`\`\`\n`,
|
|
258
262
|
{ isTTY: io.isTTY },
|
|
259
263
|
),
|
|
260
264
|
);
|
|
@@ -264,15 +268,15 @@ async function runWorkshop(
|
|
|
264
268
|
io.write(
|
|
265
269
|
renderMarkdown(
|
|
266
270
|
`\n⚠️ PoC generation failed: ${msg}\n\n` +
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
`The workshop can continue. To retry:\n` +
|
|
272
|
+
`\`\`\`\n` +
|
|
273
|
+
`sofia dev --session ${session.sessionId}\n` +
|
|
274
|
+
`\`\`\`\n`,
|
|
271
275
|
{ isTTY: io.isTTY },
|
|
272
276
|
),
|
|
273
277
|
);
|
|
274
278
|
}
|
|
275
|
-
|
|
279
|
+
|
|
276
280
|
// Continue to next phase regardless of PoC outcome
|
|
277
281
|
}
|
|
278
282
|
currentPhaseIdx = phaseOrder.indexOf(next);
|
|
@@ -47,7 +47,7 @@ function buildScaffoldPrompt(session: WorkshopSession): string {
|
|
|
47
47
|
const idea = session.ideas?.find((i) => i.id === session.selection?.ideaId);
|
|
48
48
|
const ideaTitle = idea?.title ?? session.selection?.ideaId ?? 'AI Solution';
|
|
49
49
|
const ideaDescription = idea?.description ?? 'AI-powered solution from workshop';
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
const businessContext = session.businessContext
|
|
52
52
|
? `## Business Context\n\n` +
|
|
53
53
|
`Company: ${session.businessContext.businessDescription}\n\n` +
|
|
@@ -124,40 +124,44 @@ Focus on **quality over quantity** - generate files that matter for demonstratin
|
|
|
124
124
|
*/
|
|
125
125
|
function parseCodeBlocks(response: string): Array<{ file: string; content: string }> {
|
|
126
126
|
const blocks: Array<{ file: string; content: string }> = [];
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
// Match fenced code blocks with file= attribute
|
|
129
129
|
const regex = /```(?:\w+)?\s+file=([^\s]+)\n([\s\S]*?)```/g;
|
|
130
130
|
let match;
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
while ((match = regex.exec(response)) !== null) {
|
|
133
133
|
const file = match[1].trim();
|
|
134
134
|
const content = match[2];
|
|
135
135
|
blocks.push({ file, content });
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
return blocks;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* Infer tech stack from generated files.
|
|
143
143
|
*/
|
|
144
|
-
function inferTechStack(
|
|
144
|
+
function inferTechStack(
|
|
145
|
+
files: Array<{ file: string; content: string }>,
|
|
146
|
+
): DynamicScaffoldResult['techStack'] {
|
|
145
147
|
const hasPackageJson = files.some((f) => f.file === 'package.json');
|
|
146
148
|
const hasTypeScript = files.some((f) => f.file.endsWith('.ts') || f.file === 'tsconfig.json');
|
|
147
|
-
const hasPython = files.some(
|
|
148
|
-
|
|
149
|
+
const hasPython = files.some(
|
|
150
|
+
(f) => f.file.endsWith('.py') || f.file === 'requirements.txt' || f.file === 'pyproject.toml',
|
|
151
|
+
);
|
|
152
|
+
|
|
149
153
|
// Try to parse package.json for more details
|
|
150
154
|
let testRunner = 'npm test';
|
|
151
155
|
let framework: string | undefined;
|
|
152
156
|
let buildCommand: string | undefined;
|
|
153
|
-
|
|
157
|
+
|
|
154
158
|
const packageJson = files.find((f) => f.file === 'package.json');
|
|
155
159
|
if (packageJson) {
|
|
156
160
|
try {
|
|
157
161
|
const pkg = JSON.parse(packageJson.content);
|
|
158
162
|
testRunner = pkg.scripts?.test ?? 'npm test';
|
|
159
163
|
buildCommand = pkg.scripts?.build;
|
|
160
|
-
|
|
164
|
+
|
|
161
165
|
// Detect framework from dependencies
|
|
162
166
|
if (pkg.dependencies?.['express'] || pkg.devDependencies?.['express']) framework = 'Express';
|
|
163
167
|
if (pkg.dependencies?.['react'] || pkg.devDependencies?.['react']) framework = 'React';
|
|
@@ -166,7 +170,7 @@ function inferTechStack(files: Array<{ file: string; content: string }>): Dynami
|
|
|
166
170
|
// Ignore parse errors
|
|
167
171
|
}
|
|
168
172
|
}
|
|
169
|
-
|
|
173
|
+
|
|
170
174
|
if (hasTypeScript) {
|
|
171
175
|
return {
|
|
172
176
|
language: 'TypeScript',
|
|
@@ -176,7 +180,7 @@ function inferTechStack(files: Array<{ file: string; content: string }>): Dynami
|
|
|
176
180
|
buildCommand,
|
|
177
181
|
};
|
|
178
182
|
}
|
|
179
|
-
|
|
183
|
+
|
|
180
184
|
if (hasPython) {
|
|
181
185
|
return {
|
|
182
186
|
language: 'Python',
|
|
@@ -185,7 +189,7 @@ function inferTechStack(files: Array<{ file: string; content: string }>): Dynami
|
|
|
185
189
|
buildCommand: undefined,
|
|
186
190
|
};
|
|
187
191
|
}
|
|
188
|
-
|
|
192
|
+
|
|
189
193
|
// Default to Node.js
|
|
190
194
|
return {
|
|
191
195
|
language: hasPackageJson ? 'JavaScript' : 'TypeScript',
|
|
@@ -199,7 +203,7 @@ function inferTechStack(files: Array<{ file: string; content: string }>): Dynami
|
|
|
199
203
|
|
|
200
204
|
/**
|
|
201
205
|
* Generate initial PoC scaffold using LLM.
|
|
202
|
-
*
|
|
206
|
+
*
|
|
203
207
|
* Replaces the template-based PocScaffolder with dynamic generation
|
|
204
208
|
* that's grounded in the workshop session context.
|
|
205
209
|
*/
|
|
@@ -207,49 +211,50 @@ export async function generateDynamicScaffold(
|
|
|
207
211
|
context: DynamicScaffoldContext,
|
|
208
212
|
): Promise<DynamicScaffoldResult> {
|
|
209
213
|
const { session, outputDir, client } = context;
|
|
210
|
-
|
|
214
|
+
|
|
211
215
|
// Build comprehensive prompt from session
|
|
212
216
|
const prompt = buildScaffoldPrompt(session);
|
|
213
|
-
|
|
217
|
+
|
|
214
218
|
// Create conversation session with system prompt
|
|
215
219
|
const conversationSession = await client.createSession({
|
|
216
|
-
systemPrompt:
|
|
220
|
+
systemPrompt:
|
|
221
|
+
'You are a senior software architect generating proof-of-concept project structures for AI solutions.',
|
|
217
222
|
});
|
|
218
|
-
|
|
223
|
+
|
|
219
224
|
// Send user prompt and collect response
|
|
220
225
|
let fullResponse = '';
|
|
221
226
|
const stream = conversationSession.send({
|
|
222
227
|
role: 'user',
|
|
223
228
|
content: prompt,
|
|
224
229
|
});
|
|
225
|
-
|
|
230
|
+
|
|
226
231
|
for await (const event of stream) {
|
|
227
232
|
if (event.type === 'TextDelta') {
|
|
228
233
|
fullResponse += event.text;
|
|
229
234
|
}
|
|
230
235
|
}
|
|
231
|
-
|
|
236
|
+
|
|
232
237
|
// Parse file blocks from response
|
|
233
238
|
const files = parseCodeBlocks(fullResponse);
|
|
234
|
-
|
|
239
|
+
|
|
235
240
|
if (files.length === 0) {
|
|
236
241
|
throw new Error('LLM did not generate any files. Response: ' + fullResponse.substring(0, 500));
|
|
237
242
|
}
|
|
238
|
-
|
|
243
|
+
|
|
239
244
|
// Write files to disk
|
|
240
245
|
const createdFiles: string[] = [];
|
|
241
|
-
|
|
246
|
+
|
|
242
247
|
for (const { file, content } of files) {
|
|
243
248
|
const fullPath = join(outputDir, file);
|
|
244
|
-
|
|
249
|
+
|
|
245
250
|
// Ensure directory exists
|
|
246
251
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
247
|
-
|
|
252
|
+
|
|
248
253
|
// Write file
|
|
249
254
|
await writeFile(fullPath, content, 'utf-8');
|
|
250
255
|
createdFiles.push(file);
|
|
251
256
|
}
|
|
252
|
-
|
|
257
|
+
|
|
253
258
|
// Export workshop documentation
|
|
254
259
|
try {
|
|
255
260
|
const workshopResult = await exportWorkshopDocs(session, outputDir);
|
|
@@ -257,26 +262,27 @@ export async function generateDynamicScaffold(
|
|
|
257
262
|
} catch {
|
|
258
263
|
// Non-fatal - PoC is still usable without workshop docs
|
|
259
264
|
}
|
|
260
|
-
|
|
265
|
+
|
|
261
266
|
// Create .sofia-metadata.json
|
|
262
267
|
const metadata = {
|
|
263
268
|
sessionId: session.sessionId,
|
|
264
269
|
featureSpec: '002-poc-generation',
|
|
265
270
|
generatedAt: new Date().toISOString(),
|
|
266
|
-
ideaTitle:
|
|
271
|
+
ideaTitle:
|
|
272
|
+
session.ideas?.find((i) => i.id === session.selection?.ideaId)?.title ?? 'AI Solution',
|
|
267
273
|
totalIterations: 0,
|
|
268
274
|
finalStatus: null,
|
|
269
275
|
terminationReason: null,
|
|
270
276
|
generatedBy: 'dynamic-scaffold',
|
|
271
277
|
};
|
|
272
|
-
|
|
278
|
+
|
|
273
279
|
const metadataPath = join(outputDir, '.sofia-metadata.json');
|
|
274
280
|
await writeFile(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
275
281
|
createdFiles.push('.sofia-metadata.json');
|
|
276
|
-
|
|
282
|
+
|
|
277
283
|
// Infer tech stack from generated files
|
|
278
284
|
const techStack = inferTechStack(files);
|
|
279
|
-
|
|
285
|
+
|
|
280
286
|
return {
|
|
281
287
|
createdFiles,
|
|
282
288
|
techStack,
|
package/src/develop/index.ts
CHANGED
|
@@ -5,8 +5,19 @@
|
|
|
5
5
|
* Orchestrates: scaffold → install → iterate (test → fix → repeat) → complete.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export {
|
|
9
|
-
|
|
8
|
+
export {
|
|
9
|
+
validatePocOutput,
|
|
10
|
+
initializeGitRepo,
|
|
11
|
+
scanAndRecordTodos,
|
|
12
|
+
toKebabCase,
|
|
13
|
+
buildScaffoldContext,
|
|
14
|
+
} from './pocUtils.js';
|
|
15
|
+
export type {
|
|
16
|
+
ScaffoldContext,
|
|
17
|
+
ScaffoldResult,
|
|
18
|
+
TemplateFile,
|
|
19
|
+
ValidationResult,
|
|
20
|
+
} from './pocUtils.js';
|
|
10
21
|
|
|
11
22
|
export { generateDynamicScaffold } from './dynamicScaffolder.js';
|
|
12
23
|
export type { DynamicScaffoldContext, DynamicScaffoldResult } from './dynamicScaffolder.js';
|