popeye-cli 2.1.0 → 2.7.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/dist/adapters/gemini.d.ts +14 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +41 -6
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +14 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +42 -6
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +10 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +44 -5
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.js +1 -1
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +328 -21
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +25 -2
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +21 -6
- package/dist/generators/doc-parser.d.ts.map +1 -1
- package/dist/generators/doc-parser.js +55 -4
- package/dist/generators/doc-parser.js.map +1 -1
- package/dist/generators/templates/fullstack.js +1 -1
- package/dist/generators/templates/website-components.js +1 -1
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-config.d.ts +4 -1
- package/dist/generators/templates/website-config.d.ts.map +1 -1
- package/dist/generators/templates/website-config.js +17 -11
- package/dist/generators/templates/website-config.js.map +1 -1
- package/dist/generators/templates/website-conversion.js +1 -1
- package/dist/generators/templates/website-conversion.js.map +1 -1
- package/dist/generators/templates/website-landing.js +1 -1
- package/dist/generators/templates/website-landing.js.map +1 -1
- package/dist/generators/templates/website-layout.d.ts +36 -4
- package/dist/generators/templates/website-layout.d.ts.map +1 -1
- package/dist/generators/templates/website-layout.js +466 -23
- package/dist/generators/templates/website-layout.js.map +1 -1
- package/dist/generators/templates/website-pricing.js +1 -1
- package/dist/generators/templates/website-pricing.js.map +1 -1
- package/dist/generators/templates/website-sections.js +1 -1
- package/dist/generators/templates/website-sections.js.map +1 -1
- package/dist/generators/templates/website-seo.d.ts.map +1 -1
- package/dist/generators/templates/website-seo.js +4 -1
- package/dist/generators/templates/website-seo.js.map +1 -1
- package/dist/generators/templates/website.d.ts +1 -1
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +1 -1
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-ai.d.ts +52 -0
- package/dist/generators/website-content-ai.d.ts.map +1 -0
- package/dist/generators/website-content-ai.js +141 -0
- package/dist/generators/website-content-ai.js.map +1 -0
- package/dist/generators/website-content-scanner.d.ts +1 -1
- package/dist/generators/website-content-scanner.d.ts.map +1 -1
- package/dist/generators/website-content-scanner.js +98 -1
- package/dist/generators/website-content-scanner.js.map +1 -1
- package/dist/generators/website-context.d.ts +34 -1
- package/dist/generators/website-context.d.ts.map +1 -1
- package/dist/generators/website-context.js +131 -9
- package/dist/generators/website-context.js.map +1 -1
- package/dist/generators/website-debug.d.ts +12 -0
- package/dist/generators/website-debug.d.ts.map +1 -1
- package/dist/generators/website-debug.js +16 -0
- package/dist/generators/website-debug.js.map +1 -1
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +26 -4
- package/dist/generators/website.js.map +1 -1
- package/dist/pipeline/artifact-manager.d.ts.map +1 -1
- package/dist/pipeline/artifact-manager.js +3 -0
- package/dist/pipeline/artifact-manager.js.map +1 -1
- package/dist/pipeline/auto-recovery.d.ts +56 -0
- package/dist/pipeline/auto-recovery.d.ts.map +1 -0
- package/dist/pipeline/auto-recovery.js +185 -0
- package/dist/pipeline/auto-recovery.js.map +1 -0
- package/dist/pipeline/change-request.d.ts +39 -0
- package/dist/pipeline/change-request.d.ts.map +1 -1
- package/dist/pipeline/change-request.js +40 -1
- package/dist/pipeline/change-request.js.map +1 -1
- package/dist/pipeline/check-runner.d.ts +30 -1
- package/dist/pipeline/check-runner.d.ts.map +1 -1
- package/dist/pipeline/check-runner.js +122 -1
- package/dist/pipeline/check-runner.js.map +1 -1
- package/dist/pipeline/command-resolver.d.ts.map +1 -1
- package/dist/pipeline/command-resolver.js +33 -2
- package/dist/pipeline/command-resolver.js.map +1 -1
- package/dist/pipeline/consensus/arbitrator-query.d.ts +22 -0
- package/dist/pipeline/consensus/arbitrator-query.d.ts.map +1 -0
- package/dist/pipeline/consensus/arbitrator-query.js +70 -0
- package/dist/pipeline/consensus/arbitrator-query.js.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts +131 -7
- package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -1
- package/dist/pipeline/consensus/consensus-runner.js +809 -35
- package/dist/pipeline/consensus/consensus-runner.js.map +1 -1
- package/dist/pipeline/cr-lifecycle.d.ts +42 -0
- package/dist/pipeline/cr-lifecycle.d.ts.map +1 -0
- package/dist/pipeline/cr-lifecycle.js +89 -0
- package/dist/pipeline/cr-lifecycle.js.map +1 -0
- package/dist/pipeline/gate-engine.d.ts +1 -0
- package/dist/pipeline/gate-engine.d.ts.map +1 -1
- package/dist/pipeline/gate-engine.js +27 -8
- package/dist/pipeline/gate-engine.js.map +1 -1
- package/dist/pipeline/migration.d.ts.map +1 -1
- package/dist/pipeline/migration.js +3 -26
- package/dist/pipeline/migration.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +311 -16
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.d.ts +15 -4
- package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.js +29 -17
- package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -1
- package/dist/pipeline/phases/architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/architecture.js +5 -3
- package/dist/pipeline/phases/architecture.js.map +1 -1
- package/dist/pipeline/phases/audit.d.ts.map +1 -1
- package/dist/pipeline/phases/audit.js +5 -3
- package/dist/pipeline/phases/audit.js.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.js +10 -1
- package/dist/pipeline/phases/consensus-architecture.js.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.js +10 -3
- package/dist/pipeline/phases/consensus-master-plan.js.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.js +10 -1
- package/dist/pipeline/phases/consensus-role-plans.js.map +1 -1
- package/dist/pipeline/phases/done.d.ts.map +1 -1
- package/dist/pipeline/phases/done.js +9 -4
- package/dist/pipeline/phases/done.js.map +1 -1
- package/dist/pipeline/phases/intake.d.ts +1 -0
- package/dist/pipeline/phases/intake.d.ts.map +1 -1
- package/dist/pipeline/phases/intake.js +56 -13
- package/dist/pipeline/phases/intake.js.map +1 -1
- package/dist/pipeline/phases/phase-context.d.ts +2 -0
- package/dist/pipeline/phases/phase-context.d.ts.map +1 -1
- package/dist/pipeline/phases/phase-context.js +3 -1
- package/dist/pipeline/phases/phase-context.js.map +1 -1
- package/dist/pipeline/phases/production-gate.d.ts.map +1 -1
- package/dist/pipeline/phases/production-gate.js +28 -3
- package/dist/pipeline/phases/production-gate.js.map +1 -1
- package/dist/pipeline/phases/qa-validation.d.ts.map +1 -1
- package/dist/pipeline/phases/qa-validation.js +38 -5
- package/dist/pipeline/phases/qa-validation.js.map +1 -1
- package/dist/pipeline/phases/recovery-loop.d.ts +2 -0
- package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -1
- package/dist/pipeline/phases/recovery-loop.js +200 -6
- package/dist/pipeline/phases/recovery-loop.js.map +1 -1
- package/dist/pipeline/phases/review.d.ts.map +1 -1
- package/dist/pipeline/phases/review.js +58 -28
- package/dist/pipeline/phases/review.js.map +1 -1
- package/dist/pipeline/phases/role-planning.d.ts.map +1 -1
- package/dist/pipeline/phases/role-planning.js +20 -5
- package/dist/pipeline/phases/role-planning.js.map +1 -1
- package/dist/pipeline/phases/stuck.d.ts.map +1 -1
- package/dist/pipeline/phases/stuck.js +10 -0
- package/dist/pipeline/phases/stuck.js.map +1 -1
- package/dist/pipeline/repo-snapshot.d.ts.map +1 -1
- package/dist/pipeline/repo-snapshot.js +3 -0
- package/dist/pipeline/repo-snapshot.js.map +1 -1
- package/dist/pipeline/role-execution-adapter.d.ts +2 -1
- package/dist/pipeline/role-execution-adapter.d.ts.map +1 -1
- package/dist/pipeline/role-execution-adapter.js +22 -7
- package/dist/pipeline/role-execution-adapter.js.map +1 -1
- package/dist/pipeline/skill-loader.d.ts +19 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -1
- package/dist/pipeline/skill-loader.js +22 -0
- package/dist/pipeline/skill-loader.js.map +1 -1
- package/dist/pipeline/skills/constitution-generator.d.ts +51 -0
- package/dist/pipeline/skills/constitution-generator.d.ts.map +1 -0
- package/dist/pipeline/skills/constitution-generator.js +210 -0
- package/dist/pipeline/skills/constitution-generator.js.map +1 -0
- package/dist/pipeline/skills/coverage-gate.d.ts +44 -0
- package/dist/pipeline/skills/coverage-gate.d.ts.map +1 -0
- package/dist/pipeline/skills/coverage-gate.js +143 -0
- package/dist/pipeline/skills/coverage-gate.js.map +1 -0
- package/dist/pipeline/skills/generator.d.ts +65 -0
- package/dist/pipeline/skills/generator.d.ts.map +1 -0
- package/dist/pipeline/skills/generator.js +221 -0
- package/dist/pipeline/skills/generator.js.map +1 -0
- package/dist/pipeline/skills/role-map.d.ts +38 -0
- package/dist/pipeline/skills/role-map.d.ts.map +1 -0
- package/dist/pipeline/skills/role-map.js +234 -0
- package/dist/pipeline/skills/role-map.js.map +1 -0
- package/dist/pipeline/skills/types.d.ts +47 -0
- package/dist/pipeline/skills/types.d.ts.map +1 -0
- package/dist/pipeline/skills/types.js +5 -0
- package/dist/pipeline/skills/types.js.map +1 -0
- package/dist/pipeline/skills/usage-registry.d.ts +48 -0
- package/dist/pipeline/skills/usage-registry.d.ts.map +1 -0
- package/dist/pipeline/skills/usage-registry.js +55 -0
- package/dist/pipeline/skills/usage-registry.js.map +1 -0
- package/dist/pipeline/strategy-context.d.ts +20 -0
- package/dist/pipeline/strategy-context.d.ts.map +1 -0
- package/dist/pipeline/strategy-context.js +55 -0
- package/dist/pipeline/strategy-context.js.map +1 -0
- package/dist/pipeline/type-defs/artifacts.d.ts +30 -5
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
- package/dist/pipeline/type-defs/artifacts.js +5 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -1
- package/dist/pipeline/type-defs/audit.d.ts +28 -13
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.d.ts +19 -8
- package/dist/pipeline/type-defs/checks.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.js +4 -0
- package/dist/pipeline/type-defs/checks.js.map +1 -1
- package/dist/pipeline/type-defs/packets.d.ts +119 -18
- package/dist/pipeline/type-defs/packets.d.ts.map +1 -1
- package/dist/pipeline/type-defs/packets.js +17 -1
- package/dist/pipeline/type-defs/packets.js.map +1 -1
- package/dist/pipeline/type-defs/state.d.ts +165 -16
- package/dist/pipeline/type-defs/state.d.ts.map +1 -1
- package/dist/pipeline/type-defs/state.js +26 -1
- package/dist/pipeline/type-defs/state.js.map +1 -1
- package/dist/shared/text-utils.d.ts +23 -0
- package/dist/shared/text-utils.d.ts.map +1 -0
- package/dist/shared/text-utils.js +66 -0
- package/dist/shared/text-utils.js.map +1 -0
- package/dist/shared/website-strategy-format.d.ts +18 -0
- package/dist/shared/website-strategy-format.d.ts.map +1 -0
- package/dist/shared/website-strategy-format.js +47 -0
- package/dist/shared/website-strategy-format.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +57 -8
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +1 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +1 -1
- package/dist/types/workflow.d.ts +447 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +3 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +6 -3
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +1 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts.map +1 -1
- package/dist/workflow/website-strategy.js +2 -29
- package/dist/workflow/website-strategy.js.map +1 -1
- package/dist/workflow/website-updater.d.ts.map +1 -1
- package/dist/workflow/website-updater.js +3 -2
- package/dist/workflow/website-updater.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +51 -6
- package/src/adapters/grok.ts +51 -6
- package/src/adapters/openai.ts +53 -5
- package/src/cli/commands/create.ts +1 -1
- package/src/cli/interactive.ts +337 -20
- package/src/generators/all.ts +25 -2
- package/src/generators/doc-parser.ts +75 -15
- package/src/generators/templates/fullstack.ts +1 -1
- package/src/generators/templates/website-components.ts +1 -1
- package/src/generators/templates/website-config.ts +23 -11
- package/src/generators/templates/website-conversion.ts +1 -1
- package/src/generators/templates/website-landing.ts +1 -1
- package/src/generators/templates/website-layout.ts +491 -23
- package/src/generators/templates/website-pricing.ts +1 -1
- package/src/generators/templates/website-sections.ts +1 -1
- package/src/generators/templates/website-seo.ts +4 -1
- package/src/generators/templates/website.ts +3 -0
- package/src/generators/website-content-ai.ts +186 -0
- package/src/generators/website-content-scanner.ts +113 -1
- package/src/generators/website-context.ts +151 -12
- package/src/generators/website-debug.ts +26 -0
- package/src/generators/website.ts +28 -3
- package/src/pipeline/artifact-manager.ts +3 -0
- package/src/pipeline/auto-recovery.ts +283 -0
- package/src/pipeline/change-request.ts +63 -1
- package/src/pipeline/check-runner.ts +141 -2
- package/src/pipeline/command-resolver.ts +34 -2
- package/src/pipeline/consensus/arbitrator-query.ts +101 -0
- package/src/pipeline/consensus/consensus-runner.ts +1099 -42
- package/src/pipeline/cr-lifecycle.ts +103 -0
- package/src/pipeline/gate-engine.ts +36 -8
- package/src/pipeline/migration.ts +5 -30
- package/src/pipeline/orchestrator.ts +367 -16
- package/src/pipeline/packets/consensus-packet-builder.ts +44 -18
- package/src/pipeline/phases/architecture.ts +6 -3
- package/src/pipeline/phases/audit.ts +6 -3
- package/src/pipeline/phases/consensus-architecture.ts +10 -1
- package/src/pipeline/phases/consensus-master-plan.ts +10 -3
- package/src/pipeline/phases/consensus-role-plans.ts +10 -1
- package/src/pipeline/phases/done.ts +15 -4
- package/src/pipeline/phases/intake.ts +67 -14
- package/src/pipeline/phases/phase-context.ts +6 -1
- package/src/pipeline/phases/production-gate.ts +41 -3
- package/src/pipeline/phases/qa-validation.ts +51 -5
- package/src/pipeline/phases/recovery-loop.ts +229 -7
- package/src/pipeline/phases/review.ts +73 -30
- package/src/pipeline/phases/role-planning.ts +23 -5
- package/src/pipeline/phases/stuck.ts +10 -0
- package/src/pipeline/repo-snapshot.ts +3 -0
- package/src/pipeline/role-execution-adapter.ts +30 -4
- package/src/pipeline/skill-loader.ts +33 -0
- package/src/pipeline/skills/constitution-generator.ts +236 -0
- package/src/pipeline/skills/coverage-gate.ts +199 -0
- package/src/pipeline/skills/generator.ts +287 -0
- package/src/pipeline/skills/role-map.ts +248 -0
- package/src/pipeline/skills/types.ts +53 -0
- package/src/pipeline/skills/usage-registry.ts +87 -0
- package/src/pipeline/strategy-context.ts +60 -0
- package/src/pipeline/type-defs/artifacts.ts +5 -0
- package/src/pipeline/type-defs/checks.ts +4 -0
- package/src/pipeline/type-defs/packets.ts +18 -1
- package/src/pipeline/type-defs/state.ts +26 -1
- package/src/shared/text-utils.ts +70 -0
- package/src/shared/website-strategy-format.ts +56 -0
- package/src/state/index.ts +60 -8
- package/src/types/consensus.ts +1 -0
- package/src/types/workflow.ts +6 -0
- package/src/upgrade/handlers.ts +9 -3
- package/src/workflow/consensus.ts +1 -0
- package/src/workflow/website-strategy.ts +2 -36
- package/src/workflow/website-updater.ts +4 -2
- package/tests/adapters/gemini.test.ts +165 -0
- package/tests/adapters/grok.test.ts +137 -0
- package/tests/adapters/openai.test.ts +128 -0
- package/tests/generators/doc-parser.test.ts +88 -9
- package/tests/generators/quality-gate.test.ts +19 -3
- package/tests/generators/website-components.test.ts +34 -0
- package/tests/generators/website-content-ai.test.ts +308 -0
- package/tests/generators/website-content-scanner.test.ts +86 -0
- package/tests/generators/website-context.test.ts +3 -2
- package/tests/integration/smokestack-scaffold.test.ts +385 -0
- package/tests/pipeline/auto-recovery.test.ts +337 -0
- package/tests/pipeline/change-request.test.ts +70 -0
- package/tests/pipeline/command-resolver.test.ts +42 -0
- package/tests/pipeline/consensus/arbitrator-query.test.ts +107 -0
- package/tests/pipeline/consensus-runner.test.ts +1333 -10
- package/tests/pipeline/consensus-scoring.test.ts +602 -18
- package/tests/pipeline/gate-engine.test.ts +34 -0
- package/tests/pipeline/install-check.test.ts +261 -0
- package/tests/pipeline/migration.test.ts +4 -3
- package/tests/pipeline/orchestrator.test.ts +1506 -15
- package/tests/pipeline/packets/builders.test.ts +29 -6
- package/tests/pipeline/phases/role-planning.strategy.test.ts +204 -0
- package/tests/pipeline/pipeline-persistence.test.ts +230 -0
- package/tests/pipeline/recovery-loop-guidance.test.ts +280 -0
- package/tests/pipeline/role-execution-adapter.test.ts +88 -0
- package/tests/pipeline/skills/constitution-generator.test.ts +201 -0
- package/tests/pipeline/skills/coverage-gate.test.ts +370 -0
- package/tests/pipeline/skills/generator.test.ts +213 -0
- package/tests/pipeline/skills/role-map.test.ts +198 -0
- package/tests/pipeline/skills/usage-registry.test.ts +114 -0
- package/tests/pipeline/strategy-context.test.ts +148 -0
- package/tests/shared/text-utils.test.ts +155 -0
- package/tests/state/progress-analysis.test.ts +375 -0
- package/tests/upgrade/handlers.test.ts +33 -2
- package/tests/workflow/consensus.test.ts +6 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy context loader for pipeline roles.
|
|
3
|
+
* Loads website strategy from disk and formats it for injection
|
|
4
|
+
* into role planning and execution prompts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
import type { PipelineRole } from './types.js';
|
|
11
|
+
import { WebsiteStrategySchema } from '../types/website-strategy.js';
|
|
12
|
+
import { formatWebsiteStrategy } from '../shared/website-strategy-format.js';
|
|
13
|
+
|
|
14
|
+
/** Roles that should receive website strategy context */
|
|
15
|
+
export const STRATEGY_ROLES: readonly PipelineRole[] = [
|
|
16
|
+
'WEBSITE_PROGRAMMER',
|
|
17
|
+
'MARKETING_EXPERT',
|
|
18
|
+
'SOCIAL_EXPERT',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/** Known strategy file locations, checked in order */
|
|
22
|
+
const STRATEGY_PATHS = [
|
|
23
|
+
'.popeye/website-strategy.json',
|
|
24
|
+
'.popeye/website-strategy.md',
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Load website strategy from disk and format for prompt injection.
|
|
29
|
+
* Checks known paths in order. Returns undefined if no valid strategy found.
|
|
30
|
+
*
|
|
31
|
+
* Args:
|
|
32
|
+
* projectDir: Root project directory containing .popeye/.
|
|
33
|
+
*
|
|
34
|
+
* Returns:
|
|
35
|
+
* string | undefined: Formatted strategy text, or undefined if not found/invalid.
|
|
36
|
+
*/
|
|
37
|
+
export function loadStrategyForRole(projectDir: string): string | undefined {
|
|
38
|
+
for (const relPath of STRATEGY_PATHS) {
|
|
39
|
+
const fullPath = join(projectDir, relPath);
|
|
40
|
+
if (!existsSync(fullPath)) continue;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(fullPath, 'utf-8');
|
|
44
|
+
|
|
45
|
+
// .md files: return raw content directly
|
|
46
|
+
if (relPath.endsWith('.md')) return raw;
|
|
47
|
+
|
|
48
|
+
// .json files: parse, validate, format
|
|
49
|
+
const parsed = JSON.parse(raw);
|
|
50
|
+
const strategyData = parsed.strategy ?? parsed;
|
|
51
|
+
const result = WebsiteStrategySchema.safeParse(strategyData);
|
|
52
|
+
if (!result.success) continue;
|
|
53
|
+
|
|
54
|
+
return formatWebsiteStrategy(result.data);
|
|
55
|
+
} catch {
|
|
56
|
+
continue; // Malformed file, try next path
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
@@ -32,7 +32,12 @@ export const ArtifactTypeSchema = z.enum([
|
|
|
32
32
|
'resolved_commands',
|
|
33
33
|
'constitution',
|
|
34
34
|
'change_request',
|
|
35
|
+
'recovery_fix_plan',
|
|
36
|
+
'auto_recovery_guidance',
|
|
35
37
|
'additional_context',
|
|
38
|
+
'skill_generation_log',
|
|
39
|
+
'skill_usage_log',
|
|
40
|
+
'install_check',
|
|
36
41
|
]);
|
|
37
42
|
export type ArtifactType = z.infer<typeof ArtifactTypeSchema>;
|
|
38
43
|
|
|
@@ -16,6 +16,8 @@ export const GateCheckTypeSchema = z.enum([
|
|
|
16
16
|
'placeholder_scan',
|
|
17
17
|
'start',
|
|
18
18
|
'env_check',
|
|
19
|
+
'skill_coverage',
|
|
20
|
+
'install',
|
|
19
21
|
]);
|
|
20
22
|
export type GateCheckType = z.infer<typeof GateCheckTypeSchema>;
|
|
21
23
|
|
|
@@ -42,6 +44,8 @@ export const ResolvedCommandsSchema = z.object({
|
|
|
42
44
|
typecheck: z.string().optional(),
|
|
43
45
|
migrations: z.string().optional(),
|
|
44
46
|
start: z.string().optional(),
|
|
47
|
+
install: z.string().optional(),
|
|
48
|
+
install_cwd: z.string().optional(),
|
|
45
49
|
resolved_from: z.string(),
|
|
46
50
|
});
|
|
47
51
|
export type ResolvedCommands = z.infer<typeof ResolvedCommandsSchema>;
|
|
@@ -51,8 +51,10 @@ export const ReviewerVoteSchema = z.object({
|
|
|
51
51
|
vote: z.enum(['APPROVE', 'REJECT', 'CONDITIONAL']),
|
|
52
52
|
confidence: z.number().min(0).max(1),
|
|
53
53
|
blocking_issues: z.array(z.string()),
|
|
54
|
+
required_changes: z.array(z.string()).optional(),
|
|
54
55
|
suggestions: z.array(z.string()),
|
|
55
56
|
evidence_refs: z.array(ArtifactRefSchema),
|
|
57
|
+
reviewer_inconsistency: z.boolean().optional(),
|
|
56
58
|
});
|
|
57
59
|
export type ReviewerVote = z.infer<typeof ReviewerVoteSchema>;
|
|
58
60
|
|
|
@@ -69,6 +71,7 @@ export const ConsensusResultSchema = z.object({
|
|
|
69
71
|
score: z.number().min(0).max(1),
|
|
70
72
|
weighted_score: z.number().min(0).max(1),
|
|
71
73
|
participating_reviewers: z.number().int(),
|
|
74
|
+
has_true_blockers: z.boolean().default(false),
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
export const ArbitratorResultSchema = z.object({
|
|
@@ -77,6 +80,13 @@ export const ArbitratorResultSchema = z.object({
|
|
|
77
80
|
artifact_ref: ArtifactRefSchema.optional(),
|
|
78
81
|
});
|
|
79
82
|
|
|
83
|
+
export const NormalizationMovesSchema = z.object({
|
|
84
|
+
tagged_blockers_demoted_to_suggestions: z.number(),
|
|
85
|
+
tagged_blockers_demoted_to_required: z.number(),
|
|
86
|
+
untagged_from_blocking_routed_to_required: z.number(),
|
|
87
|
+
forced_rejects: z.number(),
|
|
88
|
+
}).optional();
|
|
89
|
+
|
|
80
90
|
export const ConsensusPacketSchema = z.object({
|
|
81
91
|
metadata: z.object({
|
|
82
92
|
packet_id: z.string(),
|
|
@@ -89,6 +99,7 @@ export const ConsensusPacketSchema = z.object({
|
|
|
89
99
|
consensus_result: ConsensusResultSchema,
|
|
90
100
|
arbitrator_result: ArbitratorResultSchema.optional(),
|
|
91
101
|
final_status: z.enum(['APPROVED', 'REJECTED', 'ARBITRATED']),
|
|
102
|
+
normalization_moves: NormalizationMovesSchema,
|
|
92
103
|
});
|
|
93
104
|
export type ConsensusPacket = z.infer<typeof ConsensusPacketSchema>;
|
|
94
105
|
|
|
@@ -125,7 +136,13 @@ export const ChangeRequestSchema = z.object({
|
|
|
125
136
|
affected_phases: z.array(PipelinePhaseSchema),
|
|
126
137
|
risk_level: z.enum(['low', 'medium', 'high']),
|
|
127
138
|
}),
|
|
128
|
-
status: z.enum(['proposed', 'approved', 'rejected']),
|
|
139
|
+
status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
|
|
129
140
|
approval_artifact: ArtifactRefSchema.optional(),
|
|
141
|
+
/** Deterministic drift fingerprint for CR deduplication (v2.4.9) */
|
|
142
|
+
drift_key: z.string().optional(),
|
|
143
|
+
/** ISO timestamp when the CR was resolved (v2.4.9) */
|
|
144
|
+
resolved_at: z.string().optional(),
|
|
145
|
+
/** How the CR was resolved (v2.4.9) */
|
|
146
|
+
resolution: z.enum(['accepted_baseline', 'reverted', 'manual_override']).optional(),
|
|
130
147
|
});
|
|
131
148
|
export type ChangeRequest = z.infer<typeof ChangeRequestSchema>;
|
|
@@ -9,6 +9,7 @@ import { GateCheckTypeSchema, GateCheckResultSchema, ResolvedCommandsSchema, typ
|
|
|
9
9
|
import type { ArtifactType } from './artifacts.js';
|
|
10
10
|
import type { ConsensusPacket } from './packets.js';
|
|
11
11
|
import type { RCAPacket } from './packets.js';
|
|
12
|
+
// Reason: SkillUsageEvent type is defined inline in the Zod schema to keep state serialization self-contained
|
|
12
13
|
|
|
13
14
|
// ─── Gate Definition ─────────────────────────────────────
|
|
14
15
|
|
|
@@ -88,6 +89,7 @@ export const PipelineStateSchema = z.object({
|
|
|
88
89
|
missingArtifacts: z.array(ArtifactTypeSchema),
|
|
89
90
|
failedChecks: z.array(GateCheckTypeSchema),
|
|
90
91
|
consensusScore: z.number().optional(),
|
|
92
|
+
finalStatus: z.string().optional(), // v2.4.3: 'APPROVED' | 'REJECTED' | 'ARBITRATED'
|
|
91
93
|
timestamp: z.string(),
|
|
92
94
|
})),
|
|
93
95
|
gateChecks: z.record(z.string(), z.array(GateCheckResultSchema)),
|
|
@@ -97,6 +99,12 @@ export const PipelineStateSchema = z.object({
|
|
|
97
99
|
resolvedCommands: ResolvedCommandsSchema.optional(),
|
|
98
100
|
/** Tracks which phase failed, for recovery routing */
|
|
99
101
|
failedPhase: PipelinePhaseSchema.optional(),
|
|
102
|
+
/** Last rewind target from recovery — detects repeated same-target rewinds (v2.4.6) */
|
|
103
|
+
lastRewindTarget: PipelinePhaseSchema.optional(),
|
|
104
|
+
/** v2.6.0: Auto-recovery result — tracks whether arbitrator guidance was attempted before STUCK */
|
|
105
|
+
autoRecoveryResult: z.enum(['success', 'timeout', 'invalid', 'error']).optional(),
|
|
106
|
+
/** v2.7.0: Baseline failure count before recovery — for regression detection */
|
|
107
|
+
recoveryBaselineFailedCheckCount: z.number().int().min(0).optional(),
|
|
100
108
|
/** Session guidance: user steering, upgrade context, or resume instructions */
|
|
101
109
|
sessionGuidance: z.string().optional(),
|
|
102
110
|
/** Pending change requests that force re-routing to consensus phases (v1.1) */
|
|
@@ -104,7 +112,23 @@ export const PipelineStateSchema = z.object({
|
|
|
104
112
|
cr_id: z.string(),
|
|
105
113
|
change_type: z.enum(['scope', 'architecture', 'dependency', 'config', 'requirement']),
|
|
106
114
|
target_phase: PipelinePhaseSchema,
|
|
107
|
-
status: z.enum(['proposed', 'approved', 'rejected']),
|
|
115
|
+
status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
|
|
116
|
+
drift_key: z.string().optional(),
|
|
117
|
+
})).optional(),
|
|
118
|
+
/** ID of the CR currently being processed by the routed phase (v2.4.9) */
|
|
119
|
+
activeChangeRequestId: z.string().optional(),
|
|
120
|
+
/** Snapshot override: after config CR resolved, REVIEW uses this instead of CONSENSUS_ROLE_PLANS baseline (v2.4.9) */
|
|
121
|
+
baselineSnapshotOverride: ArtifactRefSchema.optional(),
|
|
122
|
+
/** Rolling loop signatures for stagnation detection (v2.4.9) */
|
|
123
|
+
lastSignatures: z.array(z.string()).optional(),
|
|
124
|
+
/** Skill usage events for coverage enforcement (v2.2.1) */
|
|
125
|
+
skillUsageEvents: z.array(z.object({
|
|
126
|
+
role: PipelineRoleSchema,
|
|
127
|
+
phase: PipelinePhaseSchema,
|
|
128
|
+
used_as: z.enum(['system_prompt', 'review_prompt', 'arbitration_prompt', 'role_context', 'planning_prompt', 'other']),
|
|
129
|
+
skill_source: z.enum(['project_override', 'defaults']),
|
|
130
|
+
skill_version: z.string().optional(),
|
|
131
|
+
timestamp: z.string(),
|
|
108
132
|
})).optional(),
|
|
109
133
|
});
|
|
110
134
|
export type PipelineState = z.infer<typeof PipelineStateSchema>;
|
|
@@ -163,5 +187,6 @@ export function createDefaultPipelineState(): PipelineState {
|
|
|
163
187
|
gateChecks: {},
|
|
164
188
|
activeRoles: [],
|
|
165
189
|
constitutionHash: '',
|
|
190
|
+
skillUsageEvents: [],
|
|
166
191
|
};
|
|
167
192
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text utilities for normalizing LLM-generated issue lists.
|
|
3
|
+
* Detects false-positive "no issues" responses that LLMs produce in
|
|
4
|
+
* BLOCKING_ISSUES fields (e.g. "No blocking issues found", "N/A", "None identified").
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Exact-match tokens that mean "nothing" after trim+lowercase */
|
|
8
|
+
const NONE_EXACT = new Set(['none', 'n/a', 'na', 'nil', 'nothing']);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Anchored phrase patterns that indicate "no issues".
|
|
12
|
+
* Tested against the cleaned text (bullet-stripped, trimmed, lowercased,
|
|
13
|
+
* trailing punctuation removed).
|
|
14
|
+
*/
|
|
15
|
+
const NONE_PHRASES: RegExp[] = [
|
|
16
|
+
/^no\s+(?:(?:blocking|critical|significant|major)\s+)*(?:issues?|concerns?|problems?|blockers?|showstoppers?)\b/,
|
|
17
|
+
/^none\s+(?:identified|found|detected|noted|observed|reported|applicable|at this time)\b/,
|
|
18
|
+
/^there\s+are\s+no\s+(?:(?:significant|major|critical|blocking)\s+)*(?:issues|concerns|problems|blockers)\b/,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Determine whether a text string is a "none-variant" — an LLM's way of
|
|
23
|
+
* saying "no blocking issues" rather than an actual issue description.
|
|
24
|
+
*
|
|
25
|
+
* @param text - Raw text from a blocking_issues list item
|
|
26
|
+
* @returns true if the text is a none-variant (should be filtered out)
|
|
27
|
+
*/
|
|
28
|
+
export function isNoneVariant(text: string): boolean {
|
|
29
|
+
// Empty / whitespace-only
|
|
30
|
+
const trimmed = text.trim();
|
|
31
|
+
if (trimmed.length === 0) return true;
|
|
32
|
+
|
|
33
|
+
// Strip leading bullet prefixes: "- ", "* ", "+ ", "1) ", "1. ", etc.
|
|
34
|
+
const stripped = trimmed.replace(/^[-*+\d.)\s]+/, '').trim();
|
|
35
|
+
if (stripped.length === 0) return true;
|
|
36
|
+
|
|
37
|
+
// Strip trailing punctuation
|
|
38
|
+
const cleaned = stripped.replace(/[.,;!]+$/, '').trim();
|
|
39
|
+
if (cleaned.length === 0) return true;
|
|
40
|
+
|
|
41
|
+
const lower = cleaned.toLowerCase();
|
|
42
|
+
|
|
43
|
+
// Exact match check
|
|
44
|
+
if (NONE_EXACT.has(lower)) return true;
|
|
45
|
+
|
|
46
|
+
// Anchored phrase check
|
|
47
|
+
for (const pattern of NONE_PHRASES) {
|
|
48
|
+
if (pattern.test(lower)) return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Length guard: >80 chars and no phrase match means it is likely a real issue
|
|
52
|
+
if (cleaned.length > 80) return false;
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Normalize an issue list by filtering out empty strings and none-variants.
|
|
59
|
+
* Intended as a drop-in replacement for the weak `.filter(i => i.toLowerCase() !== 'none')`
|
|
60
|
+
* used across adapters.
|
|
61
|
+
*
|
|
62
|
+
* @param items - Raw list items from parseList()
|
|
63
|
+
* @returns Filtered list with only genuine issues
|
|
64
|
+
*/
|
|
65
|
+
export function normalizeIssueList(items: string[]): string[] {
|
|
66
|
+
return items
|
|
67
|
+
.map((i) => i.trim())
|
|
68
|
+
.filter((i) => i.length > 0)
|
|
69
|
+
.filter((i) => !isNoneVariant(i));
|
|
70
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared website strategy formatter.
|
|
3
|
+
* Formats a WebsiteStrategyDocument as structured text for prompt injection.
|
|
4
|
+
* Used by both workflow (plan-mode consensus) and pipeline (role planning/execution).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { WebsiteStrategyDocument } from '../types/website-strategy.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format a WebsiteStrategyDocument as structured text for prompt injection.
|
|
11
|
+
*
|
|
12
|
+
* Args:
|
|
13
|
+
* strategy: The validated strategy document to format.
|
|
14
|
+
*
|
|
15
|
+
* Returns:
|
|
16
|
+
* string: Multi-line formatted text with ICP, Positioning, Messaging,
|
|
17
|
+
* SEO Keywords, Site Architecture, and Conversion Strategy sections.
|
|
18
|
+
*/
|
|
19
|
+
export function formatWebsiteStrategy(strategy: WebsiteStrategyDocument): string {
|
|
20
|
+
const lines: string[] = [];
|
|
21
|
+
|
|
22
|
+
lines.push(`### Target Customer`);
|
|
23
|
+
lines.push(`- Persona: ${strategy.icp.primaryPersona}`);
|
|
24
|
+
lines.push(`- Pain points: ${strategy.icp.painPoints.join(', ')}`);
|
|
25
|
+
lines.push('');
|
|
26
|
+
|
|
27
|
+
lines.push(`### Positioning`);
|
|
28
|
+
lines.push(`- Category: ${strategy.positioning.category}`);
|
|
29
|
+
lines.push(`- Value proposition: ${strategy.positioning.valueProposition}`);
|
|
30
|
+
lines.push(`- Differentiators: ${strategy.positioning.differentiators.join(', ')}`);
|
|
31
|
+
lines.push('');
|
|
32
|
+
|
|
33
|
+
lines.push(`### Messaging`);
|
|
34
|
+
lines.push(`- Headline: ${strategy.messaging.headline}`);
|
|
35
|
+
lines.push(`- Subheadline: ${strategy.messaging.subheadline}`);
|
|
36
|
+
lines.push('');
|
|
37
|
+
|
|
38
|
+
lines.push(`### SEO Keywords`);
|
|
39
|
+
lines.push(`- Primary: ${strategy.seoStrategy.primaryKeywords.join(', ')}`);
|
|
40
|
+
lines.push(`- Secondary: ${strategy.seoStrategy.secondaryKeywords.join(', ')}`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
|
|
43
|
+
lines.push(`### Site Architecture`);
|
|
44
|
+
for (const page of strategy.siteArchitecture.pages) {
|
|
45
|
+
lines.push(`- ${page.path} (${page.pageType}): ${page.purpose}`);
|
|
46
|
+
}
|
|
47
|
+
lines.push('');
|
|
48
|
+
|
|
49
|
+
lines.push(`### Conversion Strategy`);
|
|
50
|
+
lines.push(`- Primary CTA: "${strategy.conversionStrategy.primaryCta.text}" -> ${strategy.conversionStrategy.primaryCta.href}`);
|
|
51
|
+
lines.push(`- Secondary CTA: "${strategy.conversionStrategy.secondaryCta.text}" -> ${strategy.conversionStrategy.secondaryCta.href}`);
|
|
52
|
+
lines.push(`- Trust signals: ${strategy.conversionStrategy.trustSignals.join(', ')}`);
|
|
53
|
+
lines.push(`- Lead capture: ${strategy.conversionStrategy.leadCapture}`);
|
|
54
|
+
|
|
55
|
+
return lines.join('\n');
|
|
56
|
+
}
|
package/src/state/index.ts
CHANGED
|
@@ -578,6 +578,10 @@ export interface ProjectProgressAnalysis {
|
|
|
578
578
|
statusMismatch: boolean; // true if status='complete' but work is incomplete
|
|
579
579
|
planMismatch: boolean; // true if plan file has more tasks than state
|
|
580
580
|
|
|
581
|
+
// Pipeline state (when applicable)
|
|
582
|
+
pipelinePhase?: string; // Current pipeline phase (e.g. 'DONE', 'STUCK', 'IMPLEMENTATION')
|
|
583
|
+
pipelineTerminal: boolean; // true if pipeline is in DONE or STUCK state
|
|
584
|
+
|
|
581
585
|
// Milestone breakdown (from state)
|
|
582
586
|
totalMilestones: number;
|
|
583
587
|
completedMilestones: number;
|
|
@@ -780,14 +784,42 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
|
|
|
780
784
|
: 0;
|
|
781
785
|
|
|
782
786
|
// Determine if actually complete - must match plan if plan has more tasks
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
+
// v2.4.6: When no milestones/tasks exist in state but project was explicitly
|
|
788
|
+
// marked complete (status='complete' + phase='complete'), trust the status.
|
|
789
|
+
// completeProject() only sets both to 'complete' after build verification passes.
|
|
790
|
+
const noTrackingData = totalMilestones === 0 && totalTasks === 0;
|
|
791
|
+
const explicitlyCompleted = state.status === 'complete' && state.phase === 'complete';
|
|
792
|
+
|
|
793
|
+
// Pipeline state detection — pipeline truth overrides milestone truth.
|
|
794
|
+
// RECOVERY_LOOP is treated as stuck-like: the pipeline was interrupted mid-recovery
|
|
795
|
+
// and will just re-fail without new guidance.
|
|
796
|
+
const pipelinePhase = state.pipeline?.pipelinePhase;
|
|
797
|
+
const pipelineStuckLike = pipelinePhase === 'STUCK' || pipelinePhase === 'RECOVERY_LOOP';
|
|
798
|
+
const pipelineTerminal = pipelinePhase === 'DONE' || pipelineStuckLike;
|
|
799
|
+
const pipelineDone = pipelinePhase === 'DONE';
|
|
800
|
+
|
|
801
|
+
let isActuallyComplete: boolean;
|
|
802
|
+
if (noTrackingData && pipelinePhase) {
|
|
803
|
+
// Pipeline project: pipeline state is the source of truth.
|
|
804
|
+
// DONE = complete, STUCK = not complete.
|
|
805
|
+
// planMismatch is irrelevant — pipeline projects don't track milestones.
|
|
806
|
+
isActuallyComplete = pipelineDone;
|
|
807
|
+
} else if (noTrackingData) {
|
|
808
|
+
// Legacy project with no tracking: trust explicit completion only
|
|
809
|
+
isActuallyComplete = explicitlyCompleted && !planMismatch;
|
|
810
|
+
} else {
|
|
811
|
+
// Standard milestone-based: all milestones + tasks must be complete
|
|
812
|
+
isActuallyComplete = totalMilestones > 0 &&
|
|
813
|
+
completedMilestones === totalMilestones &&
|
|
814
|
+
completedTasks === totalTasks &&
|
|
815
|
+
!planMismatch; // Can't be complete if plan has more tasks
|
|
816
|
+
}
|
|
787
817
|
|
|
788
|
-
// Check for status mismatch
|
|
818
|
+
// Check for status mismatch.
|
|
819
|
+
// Pipeline terminal states are handled separately — not "mismatches".
|
|
789
820
|
const statusMismatch = (state.status === 'complete' || state.phase === 'complete') &&
|
|
790
|
-
(!isActuallyComplete || planMismatch)
|
|
821
|
+
(!isActuallyComplete || planMismatch) &&
|
|
822
|
+
!pipelineTerminal;
|
|
791
823
|
|
|
792
824
|
// Find next items to work on
|
|
793
825
|
let nextMilestone: { id: string; name: string } | undefined;
|
|
@@ -836,6 +868,15 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
|
|
|
836
868
|
if (planMismatch) {
|
|
837
869
|
progressSummary = `PLAN MISMATCH: State has ${completedTasks}/${totalTasks} tasks but plan has ${planTaskCount} tasks. ` +
|
|
838
870
|
`Only ${percentComplete}% of plan completed.`;
|
|
871
|
+
} else if (isActuallyComplete && pipelineDone && noTrackingData) {
|
|
872
|
+
progressSummary = 'Project completed via pipeline';
|
|
873
|
+
} else if (isActuallyComplete && noTrackingData) {
|
|
874
|
+
progressSummary = 'Project completed (no milestone tracking data)';
|
|
875
|
+
} else if (pipelineStuckLike) {
|
|
876
|
+
const failedAt = state.pipeline?.failedPhase ?? 'unknown';
|
|
877
|
+
const recoveryCount = state.pipeline?.recoveryCount ?? 0;
|
|
878
|
+
const label = pipelinePhase === 'RECOVERY_LOOP' ? 'PIPELINE STUCK (in recovery)' : 'PIPELINE STUCK';
|
|
879
|
+
progressSummary = `${label}: Failed at ${failedAt} after ${recoveryCount} recovery attempts`;
|
|
839
880
|
} else if (isActuallyComplete) {
|
|
840
881
|
progressSummary = `All ${totalTasks} tasks complete across ${totalMilestones} milestones`;
|
|
841
882
|
} else if (statusMismatch) {
|
|
@@ -848,6 +889,8 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
|
|
|
848
889
|
isActuallyComplete,
|
|
849
890
|
statusMismatch,
|
|
850
891
|
planMismatch,
|
|
892
|
+
pipelinePhase,
|
|
893
|
+
pipelineTerminal,
|
|
851
894
|
totalMilestones,
|
|
852
895
|
completedMilestones,
|
|
853
896
|
inProgressMilestones,
|
|
@@ -927,6 +970,15 @@ export async function verifyProjectCompletion(projectDir: string): Promise<{
|
|
|
927
970
|
* @returns Updated state
|
|
928
971
|
*/
|
|
929
972
|
export async function resetIncompleteProject(projectDir: string): Promise<ProjectState> {
|
|
973
|
+
const current = await loadProject(projectDir);
|
|
974
|
+
|
|
975
|
+
// Pipeline projects should not be reset via legacy path —
|
|
976
|
+
// legacy reset changes phase/status but not pipeline.pipelinePhase,
|
|
977
|
+
// creating inconsistent state. Pipeline resume handles its own recovery.
|
|
978
|
+
if (current.pipeline?.pipelinePhase) {
|
|
979
|
+
return current;
|
|
980
|
+
}
|
|
981
|
+
|
|
930
982
|
const verification = await verifyProjectCompletion(projectDir);
|
|
931
983
|
|
|
932
984
|
if (verification.isComplete) {
|
|
@@ -955,8 +1007,8 @@ export async function resetIncompleteProject(projectDir: string): Promise<Projec
|
|
|
955
1007
|
}
|
|
956
1008
|
|
|
957
1009
|
// Reset any failed tasks to pending for retry
|
|
958
|
-
const
|
|
959
|
-
const updatedMilestones =
|
|
1010
|
+
const latestState = await loadProject(projectDir);
|
|
1011
|
+
const updatedMilestones = latestState.milestones.map(m => ({
|
|
960
1012
|
...m,
|
|
961
1013
|
// Reset milestone status if it was incorrectly marked complete
|
|
962
1014
|
status: m.tasks.every(t => t.status === 'complete')
|
package/src/types/consensus.ts
CHANGED
package/src/types/workflow.ts
CHANGED
|
@@ -11,6 +11,8 @@ import type { TestPlanOutput } from './tester.js';
|
|
|
11
11
|
import { TestPlanOutputSchema, TestVerdictSchema } from './tester.js';
|
|
12
12
|
import type { DbConfig } from './database.js';
|
|
13
13
|
import { DbConfigSchema } from './database.js';
|
|
14
|
+
import { PipelineStateSchema } from '../pipeline/types.js';
|
|
15
|
+
import type { PipelineState } from '../pipeline/types.js';
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Workflow phases
|
|
@@ -244,6 +246,8 @@ export interface ProjectState {
|
|
|
244
246
|
auditLastRunAt?: string;
|
|
245
247
|
/** Unique identifier for the audit run */
|
|
246
248
|
auditRunId?: string;
|
|
249
|
+
/** Pipeline execution state — persisted for resume across sessions (v2.4.5) */
|
|
250
|
+
pipeline?: PipelineState;
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
/**
|
|
@@ -271,6 +275,7 @@ export const ProjectStateSchema = z.object({
|
|
|
271
275
|
analysis: z.string(),
|
|
272
276
|
strengths: z.array(z.string()),
|
|
273
277
|
concerns: z.array(z.string()),
|
|
278
|
+
blockingIssues: z.array(z.string()).default([]),
|
|
274
279
|
recommendations: z.array(z.string()),
|
|
275
280
|
approved: z.boolean(),
|
|
276
281
|
rawResponse: z.string(),
|
|
@@ -296,6 +301,7 @@ export const ProjectStateSchema = z.object({
|
|
|
296
301
|
auditRecoveryInProgress: z.boolean().optional(),
|
|
297
302
|
auditLastRunAt: z.string().optional(),
|
|
298
303
|
auditRunId: z.string().optional(),
|
|
304
|
+
pipeline: PipelineStateSchema.optional(),
|
|
299
305
|
});
|
|
300
306
|
|
|
301
307
|
/**
|
package/src/upgrade/handlers.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
generateRootDockerCompose,
|
|
22
22
|
} from '../generators/templates/fullstack.js';
|
|
23
23
|
import { loadState, saveState } from '../state/persistence.js';
|
|
24
|
-
import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext } from '../generators/website-context.js';
|
|
24
|
+
import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext, extractDocPathsFromText } from '../generators/website-context.js';
|
|
25
25
|
import type { WebsiteContentContext } from '../generators/website-context.js';
|
|
26
26
|
import { resolveWorkspaceRoot } from '../generators/workspace-root.js';
|
|
27
27
|
import { loadWebsiteStrategy } from '../workflow/website-strategy.js';
|
|
@@ -61,11 +61,17 @@ export async function buildUpgradeContentContext(
|
|
|
61
61
|
projectName: string,
|
|
62
62
|
): Promise<{ context?: WebsiteContentContext; warning?: string }> {
|
|
63
63
|
try {
|
|
64
|
+
// Load state once for idea text + brand context
|
|
65
|
+
const state = await loadState(projectDir);
|
|
66
|
+
|
|
64
67
|
// Build context from user docs (scans projectDir + parent via getScanDirectories)
|
|
65
|
-
|
|
68
|
+
// Extract extra doc paths from state idea text if available
|
|
69
|
+
const extraDocPaths = state?.idea ? extractDocPathsFromText(state.idea) : [];
|
|
70
|
+
const context = await buildWebsiteContext(
|
|
71
|
+
projectDir, projectName, state?.specification ?? state?.idea, extraDocPaths,
|
|
72
|
+
);
|
|
66
73
|
|
|
67
74
|
// Apply brand context from state if available
|
|
68
|
-
const state = await loadState(projectDir);
|
|
69
75
|
if (state?.brandContext?.primaryColor) {
|
|
70
76
|
context.brand = { ...context.brand, primaryColor: state.brandContext.primaryColor };
|
|
71
77
|
}
|
|
@@ -1176,6 +1176,7 @@ export async function runOptimizedConsensusProcess(
|
|
|
1176
1176
|
score: combinedScore,
|
|
1177
1177
|
analysis: combinedAnalysis,
|
|
1178
1178
|
concerns: allConcerns,
|
|
1179
|
+
blockingIssues: [],
|
|
1179
1180
|
recommendations: allRecommendations,
|
|
1180
1181
|
approved: combinedScore >= threshold,
|
|
1181
1182
|
strengths: [],
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
BrandAssetsContract,
|
|
14
14
|
} from '../types/website-strategy.js';
|
|
15
15
|
import { WebsiteStrategySchema } from '../types/website-strategy.js';
|
|
16
|
+
import { formatWebsiteStrategy } from '../shared/website-strategy-format.js';
|
|
16
17
|
import { createClient } from '../adapters/openai.js';
|
|
17
18
|
|
|
18
19
|
/** File name for persisted strategy */
|
|
@@ -187,42 +188,7 @@ Respond with ONLY valid JSON, no markdown code fences or explanation.`;
|
|
|
187
188
|
export function formatStrategyForPlanContext(
|
|
188
189
|
strategy: WebsiteStrategyDocument
|
|
189
190
|
): string {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
lines.push(`### Target Customer`);
|
|
193
|
-
lines.push(`- Persona: ${strategy.icp.primaryPersona}`);
|
|
194
|
-
lines.push(`- Pain points: ${strategy.icp.painPoints.join(', ')}`);
|
|
195
|
-
lines.push('');
|
|
196
|
-
|
|
197
|
-
lines.push(`### Positioning`);
|
|
198
|
-
lines.push(`- Category: ${strategy.positioning.category}`);
|
|
199
|
-
lines.push(`- Value proposition: ${strategy.positioning.valueProposition}`);
|
|
200
|
-
lines.push(`- Differentiators: ${strategy.positioning.differentiators.join(', ')}`);
|
|
201
|
-
lines.push('');
|
|
202
|
-
|
|
203
|
-
lines.push(`### Messaging`);
|
|
204
|
-
lines.push(`- Headline: ${strategy.messaging.headline}`);
|
|
205
|
-
lines.push(`- Subheadline: ${strategy.messaging.subheadline}`);
|
|
206
|
-
lines.push('');
|
|
207
|
-
|
|
208
|
-
lines.push(`### SEO Keywords`);
|
|
209
|
-
lines.push(`- Primary: ${strategy.seoStrategy.primaryKeywords.join(', ')}`);
|
|
210
|
-
lines.push(`- Secondary: ${strategy.seoStrategy.secondaryKeywords.join(', ')}`);
|
|
211
|
-
lines.push('');
|
|
212
|
-
|
|
213
|
-
lines.push(`### Site Architecture`);
|
|
214
|
-
for (const page of strategy.siteArchitecture.pages) {
|
|
215
|
-
lines.push(`- ${page.path} (${page.pageType}): ${page.purpose}`);
|
|
216
|
-
}
|
|
217
|
-
lines.push('');
|
|
218
|
-
|
|
219
|
-
lines.push(`### Conversion Strategy`);
|
|
220
|
-
lines.push(`- Primary CTA: "${strategy.conversionStrategy.primaryCta.text}" -> ${strategy.conversionStrategy.primaryCta.href}`);
|
|
221
|
-
lines.push(`- Secondary CTA: "${strategy.conversionStrategy.secondaryCta.text}" -> ${strategy.conversionStrategy.secondaryCta.href}`);
|
|
222
|
-
lines.push(`- Trust signals: ${strategy.conversionStrategy.trustSignals.join(', ')}`);
|
|
223
|
-
lines.push(`- Lead capture: ${strategy.conversionStrategy.leadCapture}`);
|
|
224
|
-
|
|
225
|
-
return lines.join('\n');
|
|
191
|
+
return formatWebsiteStrategy(strategy);
|
|
226
192
|
}
|
|
227
193
|
|
|
228
194
|
/**
|
|
@@ -9,7 +9,7 @@ import path from 'node:path';
|
|
|
9
9
|
import type { ProjectState } from '../types/workflow.js';
|
|
10
10
|
import type { OutputLanguage } from '../types/project.js';
|
|
11
11
|
import { isWorkspace } from '../types/project.js';
|
|
12
|
-
import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext } from '../generators/website-context.js';
|
|
12
|
+
import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext, extractDocPathsFromText } from '../generators/website-context.js';
|
|
13
13
|
import { resolveWorkspaceRoot } from '../generators/workspace-root.js';
|
|
14
14
|
import { generateWebsiteLandingPage } from '../generators/templates/website-landing.js';
|
|
15
15
|
import { generateWebsitePricingPage } from '../generators/templates/website-pricing.js';
|
|
@@ -52,10 +52,12 @@ export async function updateWebsiteContent(
|
|
|
52
52
|
|
|
53
53
|
// Build content context from user docs and specification
|
|
54
54
|
const parentDir = path.dirname(projectDir);
|
|
55
|
+
const extraDocPaths = state.idea ? extractDocPathsFromText(state.idea) : [];
|
|
55
56
|
const context = await buildWebsiteContext(
|
|
56
57
|
parentDir,
|
|
57
58
|
state.name,
|
|
58
|
-
state.specification
|
|
59
|
+
state.specification,
|
|
60
|
+
extraDocPaths,
|
|
59
61
|
);
|
|
60
62
|
|
|
61
63
|
// Apply brand context from state if available
|