popeye-cli 1.10.0 → 2.1.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/CHANGELOG.md +114 -0
- package/CONTRIBUTING.md +38 -3
- package/README.md +104 -18
- package/dist/adapters/gemini.js +3 -3
- package/dist/adapters/openai.js +2 -2
- package/dist/adapters/openai.js.map +1 -1
- package/dist/auth/gemini.js +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +11 -5
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/resume.d.ts.map +1 -1
- package/dist/cli/commands/resume.js +9 -1
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +29 -3
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +7 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +1 -7
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/popeye-md.d.ts +32 -0
- package/dist/config/popeye-md.d.ts.map +1 -0
- package/dist/config/popeye-md.js +111 -0
- package/dist/config/popeye-md.js.map +1 -0
- package/dist/config/schema.d.ts +3 -21
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +21 -8
- package/dist/config/schema.js.map +1 -1
- package/dist/pipeline/artifact-manager.d.ts +47 -0
- package/dist/pipeline/artifact-manager.d.ts.map +1 -0
- package/dist/pipeline/artifact-manager.js +251 -0
- package/dist/pipeline/artifact-manager.js.map +1 -0
- package/dist/pipeline/artifact-validators.d.ts +29 -0
- package/dist/pipeline/artifact-validators.d.ts.map +1 -0
- package/dist/pipeline/artifact-validators.js +173 -0
- package/dist/pipeline/artifact-validators.js.map +1 -0
- package/dist/pipeline/bridges/review-bridge.d.ts +70 -0
- package/dist/pipeline/bridges/review-bridge.d.ts.map +1 -0
- package/dist/pipeline/bridges/review-bridge.js +266 -0
- package/dist/pipeline/bridges/review-bridge.js.map +1 -0
- package/dist/pipeline/change-request.d.ts +47 -0
- package/dist/pipeline/change-request.d.ts.map +1 -0
- package/dist/pipeline/change-request.js +91 -0
- package/dist/pipeline/change-request.js.map +1 -0
- package/dist/pipeline/check-runner.d.ts +47 -0
- package/dist/pipeline/check-runner.d.ts.map +1 -0
- package/dist/pipeline/check-runner.js +417 -0
- package/dist/pipeline/check-runner.js.map +1 -0
- package/dist/pipeline/command-resolver.d.ts +9 -0
- package/dist/pipeline/command-resolver.d.ts.map +1 -0
- package/dist/pipeline/command-resolver.js +140 -0
- package/dist/pipeline/command-resolver.js.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts +44 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.js +212 -0
- package/dist/pipeline/consensus/consensus-runner.js.map +1 -0
- package/dist/pipeline/constitution.d.ts +45 -0
- package/dist/pipeline/constitution.d.ts.map +1 -0
- package/dist/pipeline/constitution.js +82 -0
- package/dist/pipeline/constitution.js.map +1 -0
- package/dist/pipeline/gate-engine.d.ts +55 -0
- package/dist/pipeline/gate-engine.d.ts.map +1 -0
- package/dist/pipeline/gate-engine.js +270 -0
- package/dist/pipeline/gate-engine.js.map +1 -0
- package/dist/pipeline/index.d.ts +26 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +35 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/migration.d.ts +15 -0
- package/dist/pipeline/migration.d.ts.map +1 -0
- package/dist/pipeline/migration.js +76 -0
- package/dist/pipeline/migration.js.map +1 -0
- package/dist/pipeline/orchestrator.d.ts +30 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +242 -0
- package/dist/pipeline/orchestrator.js.map +1 -0
- package/dist/pipeline/packets/audit-report-builder.d.ts +11 -0
- package/dist/pipeline/packets/audit-report-builder.d.ts.map +1 -0
- package/dist/pipeline/packets/audit-report-builder.js +32 -0
- package/dist/pipeline/packets/audit-report-builder.js.map +1 -0
- package/dist/pipeline/packets/consensus-packet-builder.d.ts +35 -0
- package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -0
- package/dist/pipeline/packets/consensus-packet-builder.js +80 -0
- package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -0
- package/dist/pipeline/packets/index.d.ts +12 -0
- package/dist/pipeline/packets/index.d.ts.map +1 -0
- package/dist/pipeline/packets/index.js +8 -0
- package/dist/pipeline/packets/index.js.map +1 -0
- package/dist/pipeline/packets/plan-packet-builder.d.ts +21 -0
- package/dist/pipeline/packets/plan-packet-builder.d.ts.map +1 -0
- package/dist/pipeline/packets/plan-packet-builder.js +27 -0
- package/dist/pipeline/packets/plan-packet-builder.js.map +1 -0
- package/dist/pipeline/packets/rca-packet-builder.d.ts +19 -0
- package/dist/pipeline/packets/rca-packet-builder.d.ts.map +1 -0
- package/dist/pipeline/packets/rca-packet-builder.js +22 -0
- package/dist/pipeline/packets/rca-packet-builder.js.map +1 -0
- package/dist/pipeline/phases/architecture.d.ts +7 -0
- package/dist/pipeline/phases/architecture.d.ts.map +1 -0
- package/dist/pipeline/phases/architecture.js +60 -0
- package/dist/pipeline/phases/architecture.js.map +1 -0
- package/dist/pipeline/phases/audit.d.ts +8 -0
- package/dist/pipeline/phases/audit.d.ts.map +1 -0
- package/dist/pipeline/phases/audit.js +144 -0
- package/dist/pipeline/phases/audit.js.map +1 -0
- package/dist/pipeline/phases/consensus-architecture.d.ts +7 -0
- package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -0
- package/dist/pipeline/phases/consensus-architecture.js +84 -0
- package/dist/pipeline/phases/consensus-architecture.js.map +1 -0
- package/dist/pipeline/phases/consensus-master-plan.d.ts +7 -0
- package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -0
- package/dist/pipeline/phases/consensus-master-plan.js +81 -0
- package/dist/pipeline/phases/consensus-master-plan.js.map +1 -0
- package/dist/pipeline/phases/consensus-role-plans.d.ts +7 -0
- package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -0
- package/dist/pipeline/phases/consensus-role-plans.js +85 -0
- package/dist/pipeline/phases/consensus-role-plans.js.map +1 -0
- package/dist/pipeline/phases/done.d.ts +7 -0
- package/dist/pipeline/phases/done.d.ts.map +1 -0
- package/dist/pipeline/phases/done.js +45 -0
- package/dist/pipeline/phases/done.js.map +1 -0
- package/dist/pipeline/phases/implementation.d.ts +8 -0
- package/dist/pipeline/phases/implementation.d.ts.map +1 -0
- package/dist/pipeline/phases/implementation.js +45 -0
- package/dist/pipeline/phases/implementation.js.map +1 -0
- package/dist/pipeline/phases/index.d.ts +20 -0
- package/dist/pipeline/phases/index.d.ts.map +1 -0
- package/dist/pipeline/phases/index.js +19 -0
- package/dist/pipeline/phases/index.js.map +1 -0
- package/dist/pipeline/phases/intake.d.ts +8 -0
- package/dist/pipeline/phases/intake.d.ts.map +1 -0
- package/dist/pipeline/phases/intake.js +49 -0
- package/dist/pipeline/phases/intake.js.map +1 -0
- package/dist/pipeline/phases/phase-context.d.ts +30 -0
- package/dist/pipeline/phases/phase-context.d.ts.map +1 -0
- package/dist/pipeline/phases/phase-context.js +33 -0
- package/dist/pipeline/phases/phase-context.js.map +1 -0
- package/dist/pipeline/phases/production-gate.d.ts +8 -0
- package/dist/pipeline/phases/production-gate.d.ts.map +1 -0
- package/dist/pipeline/phases/production-gate.js +84 -0
- package/dist/pipeline/phases/production-gate.js.map +1 -0
- package/dist/pipeline/phases/qa-validation.d.ts +7 -0
- package/dist/pipeline/phases/qa-validation.d.ts.map +1 -0
- package/dist/pipeline/phases/qa-validation.js +50 -0
- package/dist/pipeline/phases/qa-validation.js.map +1 -0
- package/dist/pipeline/phases/recovery-loop.d.ts +7 -0
- package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -0
- package/dist/pipeline/phases/recovery-loop.js +93 -0
- package/dist/pipeline/phases/recovery-loop.js.map +1 -0
- package/dist/pipeline/phases/review.d.ts +8 -0
- package/dist/pipeline/phases/review.d.ts.map +1 -0
- package/dist/pipeline/phases/review.js +127 -0
- package/dist/pipeline/phases/review.js.map +1 -0
- package/dist/pipeline/phases/role-planning.d.ts +7 -0
- package/dist/pipeline/phases/role-planning.d.ts.map +1 -0
- package/dist/pipeline/phases/role-planning.js +75 -0
- package/dist/pipeline/phases/role-planning.js.map +1 -0
- package/dist/pipeline/phases/stuck.d.ts +7 -0
- package/dist/pipeline/phases/stuck.d.ts.map +1 -0
- package/dist/pipeline/phases/stuck.js +51 -0
- package/dist/pipeline/phases/stuck.js.map +1 -0
- package/dist/pipeline/repo-snapshot.d.ts +24 -0
- package/dist/pipeline/repo-snapshot.d.ts.map +1 -0
- package/dist/pipeline/repo-snapshot.js +343 -0
- package/dist/pipeline/repo-snapshot.js.map +1 -0
- package/dist/pipeline/role-execution-adapter.d.ts +59 -0
- package/dist/pipeline/role-execution-adapter.d.ts.map +1 -0
- package/dist/pipeline/role-execution-adapter.js +159 -0
- package/dist/pipeline/role-execution-adapter.js.map +1 -0
- package/dist/pipeline/skill-loader.d.ts +34 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -0
- package/dist/pipeline/skill-loader.js +156 -0
- package/dist/pipeline/skill-loader.js.map +1 -0
- package/dist/pipeline/skills/defaults.d.ts +16 -0
- package/dist/pipeline/skills/defaults.d.ts.map +1 -0
- package/dist/pipeline/skills/defaults.js +189 -0
- package/dist/pipeline/skills/defaults.js.map +1 -0
- package/dist/pipeline/type-defs/artifacts.d.ts +207 -0
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -0
- package/dist/pipeline/type-defs/artifacts.js +67 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -0
- package/dist/pipeline/type-defs/audit.d.ts +259 -0
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -0
- package/dist/pipeline/type-defs/audit.js +54 -0
- package/dist/pipeline/type-defs/audit.js.map +1 -0
- package/dist/pipeline/type-defs/checks.d.ts +82 -0
- package/dist/pipeline/type-defs/checks.d.ts.map +1 -0
- package/dist/pipeline/type-defs/checks.js +38 -0
- package/dist/pipeline/type-defs/checks.js.map +1 -0
- package/dist/pipeline/type-defs/enums.d.ts +43 -0
- package/dist/pipeline/type-defs/enums.d.ts.map +1 -0
- package/dist/pipeline/type-defs/enums.js +55 -0
- package/dist/pipeline/type-defs/enums.js.map +1 -0
- package/dist/pipeline/type-defs/index.d.ts +12 -0
- package/dist/pipeline/type-defs/index.d.ts.map +1 -0
- package/dist/pipeline/type-defs/index.js +12 -0
- package/dist/pipeline/type-defs/index.js.map +1 -0
- package/dist/pipeline/type-defs/packets.d.ts +821 -0
- package/dist/pipeline/type-defs/packets.d.ts.map +1 -0
- package/dist/pipeline/type-defs/packets.js +109 -0
- package/dist/pipeline/type-defs/packets.js.map +1 -0
- package/dist/pipeline/type-defs/snapshot.d.ts +52 -0
- package/dist/pipeline/type-defs/snapshot.d.ts.map +1 -0
- package/dist/pipeline/type-defs/snapshot.js +35 -0
- package/dist/pipeline/type-defs/snapshot.js.map +1 -0
- package/dist/pipeline/type-defs/state.d.ts +455 -0
- package/dist/pipeline/type-defs/state.d.ts.map +1 -0
- package/dist/pipeline/type-defs/state.js +90 -0
- package/dist/pipeline/type-defs/state.js.map +1 -0
- package/dist/pipeline/types.d.ts +16 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +16 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/types/audit.d.ts +6 -6
- package/dist/types/consensus.d.ts +5 -1
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +15 -4
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/project.d.ts +1 -1
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +39 -10
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +1 -7
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +1 -1
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.js +5 -5
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +52 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/website-strategy.js +1 -1
- package/dist/workflow/website-strategy.js.map +1 -1
- package/package.json +1 -1
- package/skills/PHASE_GATE_ENGINE_SPEC.md +113 -20
- package/skills/POPEYE_FULL_AUTONOMY_PIPELINE.md +66 -13
- package/src/adapters/gemini.ts +3 -3
- package/src/adapters/openai.ts +2 -2
- package/src/auth/gemini.ts +1 -1
- package/src/cli/commands/create.ts +12 -6
- package/src/cli/commands/resume.ts +9 -1
- package/src/cli/interactive.ts +32 -3
- package/src/config/defaults.ts +7 -2
- package/src/config/popeye-md.ts +139 -0
- package/src/config/schema.ts +21 -8
- package/src/pipeline/artifact-manager.ts +339 -0
- package/src/pipeline/artifact-validators.ts +224 -0
- package/src/pipeline/bridges/review-bridge.ts +371 -0
- package/src/pipeline/change-request.ts +119 -0
- package/src/pipeline/check-runner.ts +504 -0
- package/src/pipeline/command-resolver.ts +168 -0
- package/src/pipeline/consensus/consensus-runner.ts +317 -0
- package/src/pipeline/constitution.ts +109 -0
- package/src/pipeline/gate-engine.ts +347 -0
- package/src/pipeline/index.ts +82 -0
- package/src/pipeline/migration.ts +91 -0
- package/src/pipeline/orchestrator.ts +322 -0
- package/src/pipeline/packets/audit-report-builder.ts +47 -0
- package/src/pipeline/packets/consensus-packet-builder.ts +112 -0
- package/src/pipeline/packets/index.ts +15 -0
- package/src/pipeline/packets/plan-packet-builder.ts +52 -0
- package/src/pipeline/packets/rca-packet-builder.ts +38 -0
- package/src/pipeline/phases/architecture.ts +73 -0
- package/src/pipeline/phases/audit.ts +193 -0
- package/src/pipeline/phases/consensus-architecture.ts +104 -0
- package/src/pipeline/phases/consensus-master-plan.ts +100 -0
- package/src/pipeline/phases/consensus-role-plans.ts +105 -0
- package/src/pipeline/phases/done.ts +68 -0
- package/src/pipeline/phases/implementation.ts +52 -0
- package/src/pipeline/phases/index.ts +21 -0
- package/src/pipeline/phases/intake.ts +68 -0
- package/src/pipeline/phases/phase-context.ts +86 -0
- package/src/pipeline/phases/production-gate.ts +113 -0
- package/src/pipeline/phases/qa-validation.ts +63 -0
- package/src/pipeline/phases/recovery-loop.ts +120 -0
- package/src/pipeline/phases/review.ts +149 -0
- package/src/pipeline/phases/role-planning.ts +92 -0
- package/src/pipeline/phases/stuck.ts +62 -0
- package/src/pipeline/repo-snapshot.ts +395 -0
- package/src/pipeline/role-execution-adapter.ts +238 -0
- package/src/pipeline/skill-loader.ts +192 -0
- package/src/pipeline/skills/defaults.ts +215 -0
- package/src/pipeline/type-defs/artifacts.ts +82 -0
- package/src/pipeline/type-defs/audit.ts +67 -0
- package/src/pipeline/type-defs/checks.ts +47 -0
- package/src/pipeline/type-defs/enums.ts +62 -0
- package/src/pipeline/type-defs/index.ts +12 -0
- package/src/pipeline/type-defs/packets.ts +131 -0
- package/src/pipeline/type-defs/snapshot.ts +55 -0
- package/src/pipeline/type-defs/state.ts +167 -0
- package/src/pipeline/types.ts +16 -0
- package/src/types/consensus.ts +16 -4
- package/src/types/index.ts +1 -0
- package/src/types/project.ts +39 -10
- package/src/types/workflow.ts +1 -1
- package/src/upgrade/handlers.ts +5 -5
- package/src/workflow/index.ts +52 -0
- package/src/workflow/website-strategy.ts +1 -1
- package/tests/cli/model-command.test.ts +19 -9
- package/tests/config/config.test.ts +3 -3
- package/tests/config/popeye-md.test.ts +168 -0
- package/tests/pipeline/artifact-manager.test.ts +183 -0
- package/tests/pipeline/artifact-validators.test.ts +207 -0
- package/tests/pipeline/bridges/review-bridge.test.ts +243 -0
- package/tests/pipeline/change-request.test.ts +180 -0
- package/tests/pipeline/check-runner.test.ts +157 -0
- package/tests/pipeline/command-resolver.test.ts +159 -0
- package/tests/pipeline/consensus-runner.test.ts +206 -0
- package/tests/pipeline/consensus-scoring.test.ts +163 -0
- package/tests/pipeline/constitution.test.ts +122 -0
- package/tests/pipeline/gate-engine.test.ts +195 -0
- package/tests/pipeline/migration.test.ts +133 -0
- package/tests/pipeline/orchestrator.test.ts +614 -0
- package/tests/pipeline/packets/builders.test.ts +347 -0
- package/tests/pipeline/repo-snapshot.test.ts +189 -0
- package/tests/pipeline/role-execution-adapter.test.ts +299 -0
- package/tests/pipeline/session-guidance.test.ts +205 -0
- package/tests/pipeline/skill-loader.test.ts +186 -0
- package/tests/pipeline/start-env-checks.test.ts +123 -0
- package/tests/pipeline/types.test.ts +156 -0
- package/tests/types/consensus.test.ts +1 -1
- package/tests/workflow/pipeline-bootstrap.test.ts +162 -0
package/src/types/index.ts
CHANGED
package/src/types/project.ts
CHANGED
|
@@ -242,7 +242,12 @@ export interface WebsiteSpec {
|
|
|
242
242
|
/**
|
|
243
243
|
* Known OpenAI models (used for suggestions and display, not strict validation)
|
|
244
244
|
*/
|
|
245
|
-
export const KNOWN_OPENAI_MODELS = [
|
|
245
|
+
export const KNOWN_OPENAI_MODELS = [
|
|
246
|
+
'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano',
|
|
247
|
+
'o3', 'o3-mini', 'o4-mini',
|
|
248
|
+
'gpt-4o', 'gpt-4o-mini',
|
|
249
|
+
'gpt-4-turbo', 'o1-preview', 'o1-mini',
|
|
250
|
+
] as const;
|
|
246
251
|
|
|
247
252
|
/**
|
|
248
253
|
* OpenAI model schema - accepts any non-empty string to support new models
|
|
@@ -289,24 +294,48 @@ export interface GenerationOptions {
|
|
|
289
294
|
* Available OpenAI models with descriptions
|
|
290
295
|
*/
|
|
291
296
|
export const OPENAI_MODELS: Record<OpenAIModel, { description: string; recommended: string }> = {
|
|
292
|
-
'gpt-
|
|
293
|
-
description: '
|
|
297
|
+
'gpt-4.1': {
|
|
298
|
+
description: 'Smartest non-reasoning model, 1M context',
|
|
294
299
|
recommended: 'Complex projects',
|
|
295
300
|
},
|
|
301
|
+
'gpt-4.1-mini': {
|
|
302
|
+
description: 'Fast, strong instruction following',
|
|
303
|
+
recommended: 'Medium complexity',
|
|
304
|
+
},
|
|
305
|
+
'gpt-4.1-nano': {
|
|
306
|
+
description: 'Fastest, most cost-efficient',
|
|
307
|
+
recommended: 'Simple projects',
|
|
308
|
+
},
|
|
309
|
+
'o3': {
|
|
310
|
+
description: 'Strongest reasoning model',
|
|
311
|
+
recommended: 'Architectural decisions',
|
|
312
|
+
},
|
|
313
|
+
'o3-mini': {
|
|
314
|
+
description: 'Efficient reasoning',
|
|
315
|
+
recommended: 'Code review',
|
|
316
|
+
},
|
|
317
|
+
'o4-mini': {
|
|
318
|
+
description: 'Fast reasoning, best on STEM',
|
|
319
|
+
recommended: 'Technical analysis',
|
|
320
|
+
},
|
|
321
|
+
'gpt-4o': {
|
|
322
|
+
description: 'Multimodal, strong all-rounder',
|
|
323
|
+
recommended: 'General purpose',
|
|
324
|
+
},
|
|
296
325
|
'gpt-4o-mini': {
|
|
297
326
|
description: 'Fast, cost-effective',
|
|
298
|
-
recommended: 'Simple
|
|
327
|
+
recommended: 'Simple tasks',
|
|
299
328
|
},
|
|
300
329
|
'gpt-4-turbo': {
|
|
301
|
-
description: 'High capability,
|
|
302
|
-
recommended: '
|
|
330
|
+
description: 'High capability, legacy',
|
|
331
|
+
recommended: 'Backward compatibility',
|
|
303
332
|
},
|
|
304
333
|
'o1-preview': {
|
|
305
|
-
description: 'Advanced reasoning',
|
|
306
|
-
recommended: '
|
|
334
|
+
description: 'Advanced reasoning (legacy)',
|
|
335
|
+
recommended: 'Legacy reasoning tasks',
|
|
307
336
|
},
|
|
308
337
|
'o1-mini': {
|
|
309
|
-
description: 'Efficient reasoning',
|
|
310
|
-
recommended: '
|
|
338
|
+
description: 'Efficient reasoning (legacy)',
|
|
339
|
+
recommended: 'Legacy reasoning tasks',
|
|
311
340
|
},
|
|
312
341
|
};
|
package/src/types/workflow.ts
CHANGED
|
@@ -254,7 +254,7 @@ export const ProjectStateSchema = z.object({
|
|
|
254
254
|
name: z.string(),
|
|
255
255
|
idea: z.string(),
|
|
256
256
|
language: OutputLanguageSchema,
|
|
257
|
-
openaiModel: z.
|
|
257
|
+
openaiModel: z.string().min(1),
|
|
258
258
|
phase: WorkflowPhaseSchema,
|
|
259
259
|
status: ProjectStatusSchema,
|
|
260
260
|
specification: z.string().optional(),
|
package/src/upgrade/handlers.ts
CHANGED
|
@@ -349,7 +349,7 @@ export async function upgradeFullstackToAll(
|
|
|
349
349
|
idea: 'Marketing website',
|
|
350
350
|
name: projectName,
|
|
351
351
|
language: 'all',
|
|
352
|
-
openaiModel: 'gpt-
|
|
352
|
+
openaiModel: 'gpt-4.1',
|
|
353
353
|
};
|
|
354
354
|
|
|
355
355
|
// Build content context from user docs, brand assets, and strategy
|
|
@@ -421,7 +421,7 @@ export async function upgradeSingleToFullstack(
|
|
|
421
421
|
if (!(await pathExists(frontendDir))) {
|
|
422
422
|
const spec: ProjectSpec = {
|
|
423
423
|
idea: 'Frontend application', name: projectName,
|
|
424
|
-
language: 'fullstack', openaiModel: 'gpt-
|
|
424
|
+
language: 'fullstack', openaiModel: 'gpt-4.1',
|
|
425
425
|
};
|
|
426
426
|
const result = await generateTypeScriptProject(spec, path.join(projectDir, 'apps'), {
|
|
427
427
|
baseDir: frontendDir,
|
|
@@ -439,7 +439,7 @@ export async function upgradeSingleToFullstack(
|
|
|
439
439
|
if (!(await pathExists(backendDir))) {
|
|
440
440
|
const spec: ProjectSpec = {
|
|
441
441
|
idea: 'Backend API', name: projectName,
|
|
442
|
-
language: 'fullstack', openaiModel: 'gpt-
|
|
442
|
+
language: 'fullstack', openaiModel: 'gpt-4.1',
|
|
443
443
|
};
|
|
444
444
|
const result = await generatePythonProject(spec, path.join(projectDir, 'apps'), {
|
|
445
445
|
baseDir: backendDir,
|
|
@@ -508,7 +508,7 @@ export async function upgradeWebsiteToAll(
|
|
|
508
508
|
if (!(await pathExists(frontendDir))) {
|
|
509
509
|
const spec: ProjectSpec = {
|
|
510
510
|
idea: 'Frontend application', name: projectName,
|
|
511
|
-
language: 'all', openaiModel: 'gpt-
|
|
511
|
+
language: 'all', openaiModel: 'gpt-4.1',
|
|
512
512
|
};
|
|
513
513
|
const result = await generateTypeScriptProject(spec, path.join(projectDir, 'apps'), {
|
|
514
514
|
baseDir: frontendDir,
|
|
@@ -520,7 +520,7 @@ export async function upgradeWebsiteToAll(
|
|
|
520
520
|
if (!(await pathExists(backendDir))) {
|
|
521
521
|
const spec: ProjectSpec = {
|
|
522
522
|
idea: 'Backend API', name: projectName,
|
|
523
|
-
language: 'all', openaiModel: 'gpt-
|
|
523
|
+
language: 'all', openaiModel: 'gpt-4.1',
|
|
524
524
|
};
|
|
525
525
|
const result = await generatePythonProject(spec, path.join(projectDir, 'apps'), {
|
|
526
526
|
baseDir: backendDir,
|
package/src/workflow/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type TaskExecutionResult,
|
|
29
29
|
} from './execution-mode.js';
|
|
30
30
|
import { getWorkflowLogger } from './workflow-logger.js';
|
|
31
|
+
import { runPipeline, resumePipeline } from '../pipeline/orchestrator.js';
|
|
31
32
|
// Types are re-exported via export * from statements below
|
|
32
33
|
|
|
33
34
|
// Re-export submodules
|
|
@@ -99,6 +100,34 @@ export async function runWorkflow(
|
|
|
99
100
|
): Promise<WorkflowResult> {
|
|
100
101
|
const { projectDir, consensusConfig, maxRetries, onProgress } = options;
|
|
101
102
|
|
|
103
|
+
// P0-5: Pipeline mode check — use full autonomy pipeline unless legacy mode
|
|
104
|
+
const useLegacy = process.env.POPEYE_LEGACY_WORKFLOW === '1' || process.env.POPEYE_LEGACY_WORKFLOW === 'true';
|
|
105
|
+
if (!useLegacy) {
|
|
106
|
+
try {
|
|
107
|
+
// Bootstrap state if it doesn't exist yet (new projects)
|
|
108
|
+
let state = await loadProject(projectDir).catch(() => null);
|
|
109
|
+
if (!state) {
|
|
110
|
+
const { createProject } = await import('../state/index.js');
|
|
111
|
+
state = await createProject(spec, projectDir);
|
|
112
|
+
}
|
|
113
|
+
const result = await runPipeline({
|
|
114
|
+
projectDir,
|
|
115
|
+
state,
|
|
116
|
+
consensusConfig,
|
|
117
|
+
onPhaseStart: (phase) => onProgress?.('pipeline', `Starting phase: ${phase}`),
|
|
118
|
+
onProgress: (msg) => onProgress?.('pipeline', msg),
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
success: result.success,
|
|
122
|
+
state: await loadProject(projectDir).catch(() => ({} as ProjectState)),
|
|
123
|
+
error: result.error,
|
|
124
|
+
};
|
|
125
|
+
} catch {
|
|
126
|
+
// Fall through to legacy workflow on pipeline error
|
|
127
|
+
onProgress?.('workflow', 'Pipeline mode failed, falling back to legacy workflow...');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
// Initialize workflow logger
|
|
103
132
|
const logger = getWorkflowLogger(projectDir);
|
|
104
133
|
|
|
@@ -206,6 +235,29 @@ export async function resumeWorkflow(
|
|
|
206
235
|
): Promise<WorkflowResult> {
|
|
207
236
|
const { consensusConfig, maxRetries, onProgress, additionalContext } = options;
|
|
208
237
|
|
|
238
|
+
// P0-5: Pipeline mode check
|
|
239
|
+
const useLegacy = process.env.POPEYE_LEGACY_WORKFLOW === '1' || process.env.POPEYE_LEGACY_WORKFLOW === 'true';
|
|
240
|
+
if (!useLegacy) {
|
|
241
|
+
try {
|
|
242
|
+
const state = await loadProject(projectDir);
|
|
243
|
+
const result = await resumePipeline({
|
|
244
|
+
projectDir,
|
|
245
|
+
state,
|
|
246
|
+
consensusConfig,
|
|
247
|
+
additionalContext,
|
|
248
|
+
onPhaseStart: (phase) => onProgress?.('pipeline', `Resuming phase: ${phase}`),
|
|
249
|
+
onProgress: (msg) => onProgress?.('pipeline', msg),
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
success: result.success,
|
|
253
|
+
state: await loadProject(projectDir).catch(() => ({} as ProjectState)),
|
|
254
|
+
error: result.error,
|
|
255
|
+
};
|
|
256
|
+
} catch {
|
|
257
|
+
onProgress?.('workflow', 'Pipeline resume failed, falling back to legacy workflow...');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
209
261
|
// Initialize workflow logger
|
|
210
262
|
const logger = getWorkflowLogger(projectDir);
|
|
211
263
|
|
|
@@ -142,7 +142,7 @@ Respond with ONLY valid JSON, no markdown code fences or explanation.`;
|
|
|
142
142
|
onProgress?.('Generating website strategy via AI...');
|
|
143
143
|
|
|
144
144
|
const completion = await client.chat.completions.create({
|
|
145
|
-
model: 'gpt-
|
|
145
|
+
model: 'gpt-4.1',
|
|
146
146
|
messages: [{ role: 'user', content: prompt }],
|
|
147
147
|
temperature: 0.4,
|
|
148
148
|
max_tokens: 4096,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, it, expect } from 'vitest';
|
|
7
7
|
import { OpenAIModelSchema, KNOWN_OPENAI_MODELS } from '../../src/types/project.js';
|
|
8
|
-
import { GeminiModelSchema, GrokModelSchema, KNOWN_GEMINI_MODELS } from '../../src/types/consensus.js';
|
|
8
|
+
import { GeminiModelSchema, GrokModelSchema, KNOWN_GEMINI_MODELS, KNOWN_GROK_MODELS } from '../../src/types/consensus.js';
|
|
9
9
|
|
|
10
10
|
describe('OpenAI model validation', () => {
|
|
11
11
|
it('should accept known OpenAI models', () => {
|
|
@@ -17,7 +17,7 @@ describe('OpenAI model validation', () => {
|
|
|
17
17
|
it('should accept unknown/new OpenAI models (flexible)', () => {
|
|
18
18
|
expect(OpenAIModelSchema.safeParse('gpt-5').success).toBe(true);
|
|
19
19
|
expect(OpenAIModelSchema.safeParse('gpt-5.2-turbo').success).toBe(true);
|
|
20
|
-
expect(OpenAIModelSchema.safeParse('
|
|
20
|
+
expect(OpenAIModelSchema.safeParse('some-future-model').success).toBe(true);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('should reject empty string', () => {
|
|
@@ -33,8 +33,8 @@ describe('Gemini model validation', () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it('should accept unknown/new Gemini models (flexible)', () => {
|
|
36
|
-
expect(GeminiModelSchema.safeParse('gemini-2.5-pro').success).toBe(true);
|
|
37
36
|
expect(GeminiModelSchema.safeParse('gemini-3.0-ultra').success).toBe(true);
|
|
37
|
+
expect(GeminiModelSchema.safeParse('gemini-4.0-flash').success).toBe(true);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('should reject empty string', () => {
|
|
@@ -44,9 +44,9 @@ describe('Gemini model validation', () => {
|
|
|
44
44
|
|
|
45
45
|
describe('Grok model validation', () => {
|
|
46
46
|
it('should accept any non-empty string as Grok model', () => {
|
|
47
|
+
expect(GrokModelSchema.safeParse('grok-4-0709').success).toBe(true);
|
|
47
48
|
expect(GrokModelSchema.safeParse('grok-3').success).toBe(true);
|
|
48
49
|
expect(GrokModelSchema.safeParse('grok-3-mini').success).toBe(true);
|
|
49
|
-
expect(GrokModelSchema.safeParse('grok-2').success).toBe(true);
|
|
50
50
|
expect(GrokModelSchema.safeParse('some-future-model').success).toBe(true);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -62,15 +62,25 @@ describe('Grok model validation', () => {
|
|
|
62
62
|
|
|
63
63
|
describe('known models lists', () => {
|
|
64
64
|
it('should have known OpenAI models', () => {
|
|
65
|
+
expect(KNOWN_OPENAI_MODELS).toContain('gpt-4.1');
|
|
65
66
|
expect(KNOWN_OPENAI_MODELS).toContain('gpt-4o');
|
|
66
|
-
expect(KNOWN_OPENAI_MODELS).toContain('
|
|
67
|
-
expect(KNOWN_OPENAI_MODELS
|
|
67
|
+
expect(KNOWN_OPENAI_MODELS).toContain('o3');
|
|
68
|
+
expect(KNOWN_OPENAI_MODELS).toContain('o4-mini');
|
|
69
|
+
expect(KNOWN_OPENAI_MODELS.length).toBeGreaterThanOrEqual(8);
|
|
68
70
|
});
|
|
69
71
|
|
|
70
72
|
it('should have known Gemini models', () => {
|
|
73
|
+
expect(KNOWN_GEMINI_MODELS).toContain('gemini-2.5-flash');
|
|
74
|
+
expect(KNOWN_GEMINI_MODELS).toContain('gemini-2.5-pro');
|
|
71
75
|
expect(KNOWN_GEMINI_MODELS).toContain('gemini-2.0-flash');
|
|
72
|
-
expect(KNOWN_GEMINI_MODELS).
|
|
73
|
-
|
|
76
|
+
expect(KNOWN_GEMINI_MODELS.length).toBeGreaterThanOrEqual(5);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should have known Grok models', () => {
|
|
80
|
+
expect(KNOWN_GROK_MODELS).toContain('grok-4-0709');
|
|
81
|
+
expect(KNOWN_GROK_MODELS).toContain('grok-3');
|
|
82
|
+
expect(KNOWN_GROK_MODELS).toContain('grok-3-mini');
|
|
83
|
+
expect(KNOWN_GROK_MODELS.length).toBeGreaterThanOrEqual(4);
|
|
74
84
|
});
|
|
75
85
|
});
|
|
76
86
|
|
|
@@ -84,7 +94,7 @@ describe('backward compatibility', () => {
|
|
|
84
94
|
});
|
|
85
95
|
|
|
86
96
|
it('should not auto-detect non-OpenAI models as known OpenAI', () => {
|
|
87
|
-
const nonOpenAI = ['gemini-2.
|
|
97
|
+
const nonOpenAI = ['gemini-2.5-flash', 'grok-3', 'grok-4-0709'];
|
|
88
98
|
for (const model of nonOpenAI) {
|
|
89
99
|
const isKnown = (KNOWN_OPENAI_MODELS as readonly string[]).includes(model);
|
|
90
100
|
expect(isKnown).toBe(false);
|
|
@@ -23,7 +23,7 @@ describe('DEFAULT_CONFIG', () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
it('should have valid API defaults', () => {
|
|
26
|
-
expect(DEFAULT_CONFIG.apis.openai.model).toBe('gpt-
|
|
26
|
+
expect(DEFAULT_CONFIG.apis.openai.model).toBe('gpt-4.1');
|
|
27
27
|
expect(DEFAULT_CONFIG.apis.openai.temperature).toBe(0.3);
|
|
28
28
|
expect(DEFAULT_CONFIG.apis.openai.max_tokens).toBe(4096);
|
|
29
29
|
});
|
|
@@ -122,11 +122,11 @@ describe('ConfigSchema', () => {
|
|
|
122
122
|
expect(result.success).toBe(false);
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
-
it('should reject
|
|
125
|
+
it('should reject empty model string', () => {
|
|
126
126
|
const config = {
|
|
127
127
|
apis: {
|
|
128
128
|
openai: {
|
|
129
|
-
model: '
|
|
129
|
+
model: '',
|
|
130
130
|
},
|
|
131
131
|
},
|
|
132
132
|
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix C tests — readPopeyeMdConfig shared config reader.
|
|
3
|
+
* Verifies popeye.md parsing for CLI commands.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { promises as fs } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { readPopeyeMdConfig } from '../../src/config/popeye-md.js';
|
|
11
|
+
|
|
12
|
+
describe('Fix C: readPopeyeMdConfig', () => {
|
|
13
|
+
let tmpDir: string;
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'popeye-md-test-'));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return null when popeye.md does not exist', async () => {
|
|
24
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
25
|
+
expect(config).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should parse basic config with reviewer and language', async () => {
|
|
29
|
+
await fs.writeFile(
|
|
30
|
+
path.join(tmpDir, 'popeye.md'),
|
|
31
|
+
[
|
|
32
|
+
'---',
|
|
33
|
+
'language: python',
|
|
34
|
+
'reviewer: openai',
|
|
35
|
+
'arbitrator: gemini',
|
|
36
|
+
'---',
|
|
37
|
+
'',
|
|
38
|
+
'# Project Config',
|
|
39
|
+
].join('\n'),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
43
|
+
expect(config).not.toBeNull();
|
|
44
|
+
expect(config!.language).toBe('python');
|
|
45
|
+
expect(config!.reviewer).toBe('openai');
|
|
46
|
+
expect(config!.arbitrator).toBe('gemini');
|
|
47
|
+
expect(config!.enableArbitration).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should parse model fields from popeye.md', async () => {
|
|
51
|
+
await fs.writeFile(
|
|
52
|
+
path.join(tmpDir, 'popeye.md'),
|
|
53
|
+
[
|
|
54
|
+
'---',
|
|
55
|
+
'language: typescript',
|
|
56
|
+
'reviewer: gemini',
|
|
57
|
+
'arbitrator: grok',
|
|
58
|
+
'openaiModel: gpt-4o-mini',
|
|
59
|
+
'geminiModel: gemini-2.0-flash',
|
|
60
|
+
'grokModel: grok-3',
|
|
61
|
+
'---',
|
|
62
|
+
].join('\n'),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
66
|
+
expect(config).not.toBeNull();
|
|
67
|
+
expect(config!.openaiModel).toBe('gpt-4o-mini');
|
|
68
|
+
expect(config!.geminiModel).toBe('gemini-2.0-flash');
|
|
69
|
+
expect(config!.grokModel).toBe('grok-3');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle arbitrator: off', async () => {
|
|
73
|
+
await fs.writeFile(
|
|
74
|
+
path.join(tmpDir, 'popeye.md'),
|
|
75
|
+
[
|
|
76
|
+
'---',
|
|
77
|
+
'language: fullstack',
|
|
78
|
+
'reviewer: openai',
|
|
79
|
+
'arbitrator: off',
|
|
80
|
+
'---',
|
|
81
|
+
].join('\n'),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
85
|
+
expect(config).not.toBeNull();
|
|
86
|
+
expect(config!.enableArbitration).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return null when frontmatter is missing', async () => {
|
|
90
|
+
await fs.writeFile(
|
|
91
|
+
path.join(tmpDir, 'popeye.md'),
|
|
92
|
+
'# Just a markdown file\n\nNo frontmatter here.',
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
96
|
+
expect(config).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should return null when essential fields are missing', async () => {
|
|
100
|
+
await fs.writeFile(
|
|
101
|
+
path.join(tmpDir, 'popeye.md'),
|
|
102
|
+
[
|
|
103
|
+
'---',
|
|
104
|
+
'projectName: my-app',
|
|
105
|
+
'created: 2024-01-01',
|
|
106
|
+
'---',
|
|
107
|
+
].join('\n'),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
111
|
+
// Missing language and reviewer -> null
|
|
112
|
+
expect(config).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should extract notes section', async () => {
|
|
116
|
+
await fs.writeFile(
|
|
117
|
+
path.join(tmpDir, 'popeye.md'),
|
|
118
|
+
[
|
|
119
|
+
'---',
|
|
120
|
+
'language: website',
|
|
121
|
+
'reviewer: openai',
|
|
122
|
+
'---',
|
|
123
|
+
'',
|
|
124
|
+
'## Notes',
|
|
125
|
+
'This project uses Tailwind CSS.',
|
|
126
|
+
'Deploy to Vercel.',
|
|
127
|
+
].join('\n'),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
131
|
+
expect(config).not.toBeNull();
|
|
132
|
+
expect(config!.notes).toContain('Tailwind CSS');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should reject invalid language values', async () => {
|
|
136
|
+
await fs.writeFile(
|
|
137
|
+
path.join(tmpDir, 'popeye.md'),
|
|
138
|
+
[
|
|
139
|
+
'---',
|
|
140
|
+
'language: rust',
|
|
141
|
+
'reviewer: openai',
|
|
142
|
+
'---',
|
|
143
|
+
].join('\n'),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
147
|
+
// Invalid language means essential field is missing
|
|
148
|
+
expect(config).toBeNull();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should return model fields as undefined when not specified', async () => {
|
|
152
|
+
await fs.writeFile(
|
|
153
|
+
path.join(tmpDir, 'popeye.md'),
|
|
154
|
+
[
|
|
155
|
+
'---',
|
|
156
|
+
'language: python',
|
|
157
|
+
'reviewer: openai',
|
|
158
|
+
'---',
|
|
159
|
+
].join('\n'),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const config = await readPopeyeMdConfig(tmpDir);
|
|
163
|
+
expect(config).not.toBeNull();
|
|
164
|
+
expect(config!.openaiModel).toBeUndefined();
|
|
165
|
+
expect(config!.geminiModel).toBeUndefined();
|
|
166
|
+
expect(config!.grokModel).toBeUndefined();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact Manager tests — create, version, verify, index.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { mkdirSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { createArtifactManager } from '../../src/pipeline/artifact-manager.js';
|
|
9
|
+
|
|
10
|
+
const TEST_DIR = join(process.cwd(), '.test-artifact-manager');
|
|
11
|
+
|
|
12
|
+
describe('ArtifactManager', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
if (existsSync(TEST_DIR)) {
|
|
15
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
if (existsSync(TEST_DIR)) {
|
|
22
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('ensureDocsStructure', () => {
|
|
27
|
+
it('should create all required subdirectories', () => {
|
|
28
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
29
|
+
manager.ensureDocsStructure();
|
|
30
|
+
|
|
31
|
+
expect(existsSync(join(TEST_DIR, 'docs'))).toBe(true);
|
|
32
|
+
expect(existsSync(join(TEST_DIR, 'docs', 'master-plan'))).toBe(true);
|
|
33
|
+
expect(existsSync(join(TEST_DIR, 'docs', 'architecture'))).toBe(true);
|
|
34
|
+
expect(existsSync(join(TEST_DIR, 'docs', 'consensus'))).toBe(true);
|
|
35
|
+
expect(existsSync(join(TEST_DIR, 'docs', 'audit'))).toBe(true);
|
|
36
|
+
expect(existsSync(join(TEST_DIR, 'docs', 'release'))).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('createArtifactText', () => {
|
|
41
|
+
it('should create a markdown artifact file', () => {
|
|
42
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
43
|
+
const entry = manager.createArtifactText(
|
|
44
|
+
'master_plan',
|
|
45
|
+
'# Master Plan\n\nThe plan.',
|
|
46
|
+
'INTAKE',
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(entry.type).toBe('master_plan');
|
|
50
|
+
expect(entry.phase).toBe('INTAKE');
|
|
51
|
+
expect(entry.version).toBe(1);
|
|
52
|
+
expect(entry.content_type).toBe('markdown');
|
|
53
|
+
expect(entry.immutable).toBe(true);
|
|
54
|
+
expect(entry.path).toContain('master-plan');
|
|
55
|
+
expect(entry.path).toContain('.md');
|
|
56
|
+
|
|
57
|
+
// File should exist on disk
|
|
58
|
+
const fullPath = join(TEST_DIR, entry.path);
|
|
59
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
60
|
+
expect(readFileSync(fullPath, 'utf-8')).toBe('# Master Plan\n\nThe plan.');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('createArtifactJson', () => {
|
|
65
|
+
it('should create a JSON artifact file', () => {
|
|
66
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
67
|
+
const data = { key: 'value', count: 42 };
|
|
68
|
+
const entry = manager.createArtifactJson(
|
|
69
|
+
'repo_snapshot',
|
|
70
|
+
data,
|
|
71
|
+
'INTAKE',
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(entry.content_type).toBe('json');
|
|
75
|
+
expect(entry.path).toContain('.json');
|
|
76
|
+
|
|
77
|
+
const fullPath = join(TEST_DIR, entry.path);
|
|
78
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
79
|
+
|
|
80
|
+
const parsed = JSON.parse(readFileSync(fullPath, 'utf-8'));
|
|
81
|
+
expect(parsed.key).toBe('value');
|
|
82
|
+
expect(parsed.count).toBe(42);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('verifyArtifact', () => {
|
|
87
|
+
it('should verify intact artifacts', () => {
|
|
88
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
89
|
+
const entry = manager.createArtifactText(
|
|
90
|
+
'master_plan',
|
|
91
|
+
'Content here',
|
|
92
|
+
'INTAKE',
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
expect(manager.verifyArtifact(entry)).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should detect missing artifacts', () => {
|
|
99
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
100
|
+
const entry = manager.createArtifactText(
|
|
101
|
+
'master_plan',
|
|
102
|
+
'Content',
|
|
103
|
+
'INTAKE',
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Remove the file
|
|
107
|
+
rmSync(join(TEST_DIR, entry.path));
|
|
108
|
+
expect(manager.verifyArtifact(entry)).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('version chains', () => {
|
|
113
|
+
it('should auto-increment versions within a group', () => {
|
|
114
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
115
|
+
const groupId = 'test-group';
|
|
116
|
+
|
|
117
|
+
const v1 = manager.createAndStoreText('master_plan', 'v1 content', 'INTAKE', groupId);
|
|
118
|
+
const v2 = manager.createAndStoreText('master_plan', 'v2 content', 'INTAKE', groupId);
|
|
119
|
+
const v3 = manager.createAndStoreText('master_plan', 'v3 content', 'INTAKE', groupId);
|
|
120
|
+
|
|
121
|
+
expect(v1.version).toBe(1);
|
|
122
|
+
expect(v2.version).toBe(2);
|
|
123
|
+
expect(v3.version).toBe(3);
|
|
124
|
+
|
|
125
|
+
expect(v1.group_id).toBe(groupId);
|
|
126
|
+
expect(v2.group_id).toBe(groupId);
|
|
127
|
+
expect(v3.group_id).toBe(groupId);
|
|
128
|
+
|
|
129
|
+
expect(v2.previous_id).toBe(v1.id);
|
|
130
|
+
expect(v3.previous_id).toBe(v2.id);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should start at v1 for a new group', () => {
|
|
134
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
135
|
+
const entry = manager.createAndStoreText('architecture', 'content', 'ARCHITECTURE');
|
|
136
|
+
expect(entry.version).toBe(1);
|
|
137
|
+
expect(entry.previous_id).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('toArtifactRef', () => {
|
|
142
|
+
it('should convert entry to ref', () => {
|
|
143
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
144
|
+
const entry = manager.createArtifactText('master_plan', 'content', 'INTAKE');
|
|
145
|
+
const ref = manager.toArtifactRef(entry);
|
|
146
|
+
|
|
147
|
+
expect(ref.artifact_id).toBe(entry.id);
|
|
148
|
+
expect(ref.path).toBe(entry.path);
|
|
149
|
+
expect(ref.sha256).toBe(entry.sha256);
|
|
150
|
+
expect(ref.version).toBe(entry.version);
|
|
151
|
+
expect(ref.type).toBe(entry.type);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('updateIndex', () => {
|
|
156
|
+
it('should create INDEX.md with artifact listing', () => {
|
|
157
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
158
|
+
const entry = manager.createArtifactText('master_plan', 'content', 'INTAKE');
|
|
159
|
+
|
|
160
|
+
manager.updateIndex([entry]);
|
|
161
|
+
|
|
162
|
+
const indexPath = join(TEST_DIR, 'docs', 'INDEX.md');
|
|
163
|
+
expect(existsSync(indexPath)).toBe(true);
|
|
164
|
+
|
|
165
|
+
const indexContent = readFileSync(indexPath, 'utf-8');
|
|
166
|
+
expect(indexContent).toContain('Documentation Index');
|
|
167
|
+
expect(indexContent).toContain('master_plan');
|
|
168
|
+
expect(indexContent).toContain('INTAKE');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('storeArtifactMetadata', () => {
|
|
173
|
+
it('should store and retrieve metadata', () => {
|
|
174
|
+
const manager = createArtifactManager(TEST_DIR);
|
|
175
|
+
const entry = manager.createArtifactText('master_plan', 'content', 'INTAKE');
|
|
176
|
+
manager.storeArtifactMetadata(entry);
|
|
177
|
+
|
|
178
|
+
const listed = manager.listArtifacts('master_plan');
|
|
179
|
+
expect(listed).toHaveLength(1);
|
|
180
|
+
expect(listed[0].id).toBe(entry.id);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|