gaslighting-engine 0.3.0 → 0.4.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/README.md +147 -18
- package/desktop-dist/assets/index-BoI6eWtN.js +44 -0
- package/desktop-dist/assets/index-oFTvTTu8.css +1 -0
- package/desktop-dist/index.html +13 -0
- package/dist/agent-runtimes/permissions.js +34 -0
- package/dist/agent-runtimes/prompts.js +30 -0
- package/dist/agent-runtimes/registry.js +214 -0
- package/dist/agent-runtimes/types.js +1 -0
- package/dist/cli.js +145 -4
- package/dist/commands/agent.js +11 -0
- package/dist/commands/apiRun.js +32 -0
- package/dist/commands/cockpit.js +61 -7
- package/dist/commands/desktop.js +37 -0
- package/dist/commands/doctor.js +8 -1
- package/dist/commands/loop.js +64 -0
- package/dist/commands/mcp.js +43 -0
- package/dist/commands/skill.js +23 -0
- package/dist/core/cockpitHtml.js +4 -2
- package/dist/core/codexRuntime.js +44 -32
- package/dist/core/generateDocs.js +16 -10
- package/dist/core/generateOtherMarkdown.js +27 -27
- package/dist/core/generateStructuredDocs.js +405 -0
- package/dist/core/mcpRegistry.js +103 -0
- package/dist/core/missionLoop.js +179 -0
- package/dist/core/skillRegistry.js +62 -0
- package/dist/desktop/main.js +186 -0
- package/dist/desktop/preload.js +22 -0
- package/dist/utils/logger.js +1 -1
- package/dist/version.js +1 -1
- package/docs/codex-usage.md +36 -3
- package/examples/ecommerce/.agents/config.json +10 -0
- package/examples/ecommerce/.agents/mcp/manifest.json +5 -0
- package/examples/ecommerce/.agents/runtimes/claude.json +6 -0
- package/examples/ecommerce/.agents/runtimes/codex.json +6 -0
- package/examples/ecommerce/.agents/runtimes/kimi.json +6 -0
- package/examples/ecommerce/.agents/runtimes/local.json +6 -0
- package/examples/ecommerce/.agents/runtimes/openai_compatible.json +6 -0
- package/examples/ecommerce/.agents/runtimes/opencode.json +6 -0
- package/examples/ecommerce/.agents/runtimes/qwen.json +6 -0
- package/examples/ecommerce/.codex/prompts/gaslighting.md +9 -9
- package/examples/ecommerce/.gaslighting/AGENTS.md +9 -9
- package/examples/ecommerce/.gaslighting/AGENT_RUNTIME.md +1 -1
- package/examples/ecommerce/.gaslighting/CODEX_PROMPT.md +9 -9
- package/examples/ecommerce/.gaslighting/DECISION_LOG.md +1 -1
- package/examples/ecommerce/.gaslighting/GASLIGHTING.md +2 -2
- package/examples/ecommerce/.gaslighting/INDEX.md +39 -0
- package/examples/ecommerce/.gaslighting/MEMORY.md +1 -1
- package/examples/ecommerce/.gaslighting/PRD.md +1 -1
- package/examples/ecommerce/.gaslighting/PROJECT_CARE.md +1 -1
- package/examples/ecommerce/.gaslighting/checkpoints/latest.md +7 -0
- package/examples/ecommerce/.gaslighting/config.json +7 -0
- package/examples/ecommerce/.gaslighting/decisions/ADR-0001-stack.md +11 -0
- package/examples/ecommerce/.gaslighting/decisions/ADR-0002-agent-runtime.md +11 -0
- package/examples/ecommerce/.gaslighting/features/core-scope/DECISIONS.md +5 -0
- package/examples/ecommerce/.gaslighting/features/core-scope/FEATURE.md +7 -0
- package/examples/ecommerce/.gaslighting/features/core-scope/RISKS.md +5 -0
- package/examples/ecommerce/.gaslighting/features/core-scope/TASKS.md +18 -0
- package/examples/ecommerce/.gaslighting/loops/current-session.json +9 -0
- package/examples/ecommerce/.gaslighting/memory/OPEN_QUESTIONS.md +5 -0
- package/examples/ecommerce/.gaslighting/memory/PROJECT_MEMORY.md +5 -0
- package/examples/ecommerce/.gaslighting/memory/SESSION_LOG.md +3 -0
- package/examples/ecommerce/.gaslighting/mission/COMPLETION_STANDARD.md +11 -0
- package/examples/ecommerce/.gaslighting/mission/EXECUTION_RULES.md +20 -0
- package/examples/ecommerce/.gaslighting/mission/PROJECT_PURPOSE.md +20 -0
- package/examples/ecommerce/.gaslighting/operations/DEPLOYMENT.md +6 -0
- package/examples/ecommerce/.gaslighting/operations/DOMAIN.md +6 -0
- package/examples/ecommerce/.gaslighting/operations/ENV.md +5 -0
- package/examples/ecommerce/.gaslighting/operations/GIT.md +7 -0
- package/examples/ecommerce/.gaslighting/operations/GITHUB.md +7 -0
- package/examples/ecommerce/.gaslighting/operations/PROJECT_CARE.md +76 -0
- package/examples/ecommerce/.gaslighting/pages/admin-products/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/admin-products/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/cart/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/cart/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/checkout/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/checkout/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/home/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/home/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/my-orders/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/my-orders/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/order-complete/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/order-complete/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/product-detail/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/product-detail/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/pages/product-list/ACCEPTANCE.md +7 -0
- package/examples/ecommerce/.gaslighting/pages/product-list/PAGE_PRD.md +15 -0
- package/examples/ecommerce/.gaslighting/product/PAGE_MAP.md +11 -0
- package/examples/ecommerce/.gaslighting/product/PRD.md +115 -0
- package/examples/ecommerce/.gaslighting/product/USER_FLOWS.md +22 -0
- package/examples/ecommerce/.gaslighting/tasks/TASK-0001.md +17 -0
- package/examples/ecommerce/.gaslighting/verification/latest-report.md +3 -0
- package/examples/ecommerce/AGENTS.md +12 -10
- package/examples/hospital-homepage/.agents/config.json +10 -0
- package/examples/hospital-homepage/.agents/mcp/manifest.json +5 -0
- package/examples/hospital-homepage/.agents/runtimes/claude.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/codex.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/kimi.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/local.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/openai_compatible.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/opencode.json +6 -0
- package/examples/hospital-homepage/.agents/runtimes/qwen.json +6 -0
- package/examples/hospital-homepage/.codex/prompts/gaslighting.md +9 -9
- package/examples/hospital-homepage/.gaslighting/AGENTS.md +9 -9
- package/examples/hospital-homepage/.gaslighting/CODEX_PROMPT.md +9 -9
- package/examples/hospital-homepage/.gaslighting/INDEX.md +37 -0
- package/examples/hospital-homepage/.gaslighting/checkpoints/latest.md +7 -0
- package/examples/hospital-homepage/.gaslighting/config.json +7 -0
- package/examples/hospital-homepage/.gaslighting/decisions/ADR-0001-stack.md +11 -0
- package/examples/hospital-homepage/.gaslighting/decisions/ADR-0002-agent-runtime.md +11 -0
- package/examples/hospital-homepage/.gaslighting/features/core-scope/DECISIONS.md +5 -0
- package/examples/hospital-homepage/.gaslighting/features/core-scope/FEATURE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/features/core-scope/RISKS.md +5 -0
- package/examples/hospital-homepage/.gaslighting/features/core-scope/TASKS.md +17 -0
- package/examples/hospital-homepage/.gaslighting/loops/current-session.json +9 -0
- package/examples/hospital-homepage/.gaslighting/memory/OPEN_QUESTIONS.md +5 -0
- package/examples/hospital-homepage/.gaslighting/memory/PROJECT_MEMORY.md +5 -0
- package/examples/hospital-homepage/.gaslighting/memory/SESSION_LOG.md +3 -0
- package/examples/hospital-homepage/.gaslighting/mission/COMPLETION_STANDARD.md +11 -0
- package/examples/hospital-homepage/.gaslighting/mission/EXECUTION_RULES.md +20 -0
- package/examples/hospital-homepage/.gaslighting/mission/PROJECT_PURPOSE.md +20 -0
- package/examples/hospital-homepage/.gaslighting/operations/DEPLOYMENT.md +6 -0
- package/examples/hospital-homepage/.gaslighting/operations/DOMAIN.md +6 -0
- package/examples/hospital-homepage/.gaslighting/operations/ENV.md +5 -0
- package/examples/hospital-homepage/.gaslighting/operations/GIT.md +7 -0
- package/examples/hospital-homepage/.gaslighting/operations/GITHUB.md +7 -0
- package/examples/hospital-homepage/.gaslighting/operations/PROJECT_CARE.md +76 -0
- package/examples/hospital-homepage/.gaslighting/pages/consultation-contact/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/consultation-contact/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/departments-treatments/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/departments-treatments/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/doctor-profiles/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/doctor-profiles/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/home/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/home/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/hospital-introduction/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/hospital-introduction/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/location/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/location/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/pages/privacy-policy/ACCEPTANCE.md +7 -0
- package/examples/hospital-homepage/.gaslighting/pages/privacy-policy/PAGE_PRD.md +15 -0
- package/examples/hospital-homepage/.gaslighting/product/PAGE_MAP.md +9 -0
- package/examples/hospital-homepage/.gaslighting/product/PRD.md +119 -0
- package/examples/hospital-homepage/.gaslighting/product/USER_FLOWS.md +20 -0
- package/examples/hospital-homepage/.gaslighting/tasks/TASK-0001.md +17 -0
- package/examples/hospital-homepage/.gaslighting/verification/latest-report.md +3 -0
- package/examples/hospital-homepage/AGENTS.md +12 -10
- package/examples/landing-page/.agents/config.json +10 -0
- package/examples/landing-page/.agents/mcp/manifest.json +5 -0
- package/examples/landing-page/.agents/runtimes/claude.json +6 -0
- package/examples/landing-page/.agents/runtimes/codex.json +6 -0
- package/examples/landing-page/.agents/runtimes/kimi.json +6 -0
- package/examples/landing-page/.agents/runtimes/local.json +6 -0
- package/examples/landing-page/.agents/runtimes/openai_compatible.json +6 -0
- package/examples/landing-page/.agents/runtimes/opencode.json +6 -0
- package/examples/landing-page/.agents/runtimes/qwen.json +6 -0
- package/examples/landing-page/.codex/prompts/gaslighting.md +9 -9
- package/examples/landing-page/.gaslighting/AGENTS.md +9 -9
- package/examples/landing-page/.gaslighting/AGENT_RUNTIME.md +1 -1
- package/examples/landing-page/.gaslighting/CODEX_PROMPT.md +9 -9
- package/examples/landing-page/.gaslighting/DECISION_LOG.md +1 -1
- package/examples/landing-page/.gaslighting/GASLIGHTING.md +2 -2
- package/examples/landing-page/.gaslighting/INDEX.md +31 -0
- package/examples/landing-page/.gaslighting/MEMORY.md +1 -1
- package/examples/landing-page/.gaslighting/PRD.md +1 -1
- package/examples/landing-page/.gaslighting/PROJECT_CARE.md +1 -1
- package/examples/landing-page/.gaslighting/checkpoints/latest.md +7 -0
- package/examples/landing-page/.gaslighting/config.json +7 -0
- package/examples/landing-page/.gaslighting/decisions/ADR-0001-stack.md +11 -0
- package/examples/landing-page/.gaslighting/decisions/ADR-0002-agent-runtime.md +11 -0
- package/examples/landing-page/.gaslighting/features/core-scope/DECISIONS.md +5 -0
- package/examples/landing-page/.gaslighting/features/core-scope/FEATURE.md +7 -0
- package/examples/landing-page/.gaslighting/features/core-scope/RISKS.md +5 -0
- package/examples/landing-page/.gaslighting/features/core-scope/TASKS.md +11 -0
- package/examples/landing-page/.gaslighting/loops/current-session.json +9 -0
- package/examples/landing-page/.gaslighting/memory/OPEN_QUESTIONS.md +5 -0
- package/examples/landing-page/.gaslighting/memory/PROJECT_MEMORY.md +5 -0
- package/examples/landing-page/.gaslighting/memory/SESSION_LOG.md +3 -0
- package/examples/landing-page/.gaslighting/mission/COMPLETION_STANDARD.md +11 -0
- package/examples/landing-page/.gaslighting/mission/EXECUTION_RULES.md +20 -0
- package/examples/landing-page/.gaslighting/mission/PROJECT_PURPOSE.md +19 -0
- package/examples/landing-page/.gaslighting/operations/DEPLOYMENT.md +6 -0
- package/examples/landing-page/.gaslighting/operations/DOMAIN.md +6 -0
- package/examples/landing-page/.gaslighting/operations/ENV.md +5 -0
- package/examples/landing-page/.gaslighting/operations/GIT.md +7 -0
- package/examples/landing-page/.gaslighting/operations/GITHUB.md +7 -0
- package/examples/landing-page/.gaslighting/operations/PROJECT_CARE.md +75 -0
- package/examples/landing-page/.gaslighting/pages/single-landing-page-with-hero-problem-solution-benefits-social-p/ACCEPTANCE.md +7 -0
- package/examples/landing-page/.gaslighting/pages/single-landing-page-with-hero-problem-solution-benefits-social-p/PAGE_PRD.md +15 -0
- package/examples/landing-page/.gaslighting/product/PAGE_MAP.md +3 -0
- package/examples/landing-page/.gaslighting/product/PRD.md +103 -0
- package/examples/landing-page/.gaslighting/product/USER_FLOWS.md +14 -0
- package/examples/landing-page/.gaslighting/tasks/TASK-0001.md +17 -0
- package/examples/landing-page/.gaslighting/verification/latest-report.md +3 -0
- package/examples/landing-page/AGENTS.md +12 -10
- package/package.json +22 -5
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { pageList, projectPurpose, stackSummary, decisionDate } from "./content.js";
|
|
2
|
+
import { generateProjectCareMarkdown } from "./projectCare.js";
|
|
3
|
+
import { generatePrdMarkdown } from "./generatePrdMarkdown.js";
|
|
4
|
+
export function generateStructuredDocs(input, analysis, root) {
|
|
5
|
+
const pages = pageList(analysis.projectType);
|
|
6
|
+
const purpose = projectPurpose(analysis.projectType);
|
|
7
|
+
const primaryPages = pages.slice(0, 8);
|
|
8
|
+
return [
|
|
9
|
+
{ filename: ".gaslighting/config.json", content: gaslightingConfigJson(input) },
|
|
10
|
+
{ filename: ".agents/config.json", content: agentsConfigJson(input) },
|
|
11
|
+
{ filename: ".agents/runtimes/codex.json", content: runtimeConfig("codex", true, true) },
|
|
12
|
+
{ filename: ".agents/runtimes/claude.json", content: runtimeConfig("claude", true, true) },
|
|
13
|
+
{ filename: ".agents/runtimes/qwen.json", content: runtimeConfig("qwen", true, false) },
|
|
14
|
+
{ filename: ".agents/runtimes/kimi.json", content: runtimeConfig("kimi", true, true) },
|
|
15
|
+
{ filename: ".agents/runtimes/opencode.json", content: runtimeConfig("opencode", true, false) },
|
|
16
|
+
{ filename: ".agents/runtimes/openai_compatible.json", content: runtimeConfig("openai_compatible", false, false) },
|
|
17
|
+
{ filename: ".agents/runtimes/local.json", content: runtimeConfig("local", false, false) },
|
|
18
|
+
{ filename: ".agents/mcp/manifest.json", content: mcpManifestJson() },
|
|
19
|
+
{ filename: ".gaslighting/INDEX.md", content: indexMarkdown(input, analysis, pages) },
|
|
20
|
+
{ filename: ".gaslighting/mission/PROJECT_PURPOSE.md", content: projectPurposeMarkdown(input, analysis, purpose) },
|
|
21
|
+
{ filename: ".gaslighting/mission/EXECUTION_RULES.md", content: executionRulesMarkdown(input) },
|
|
22
|
+
{ filename: ".gaslighting/mission/COMPLETION_STANDARD.md", content: completionStandardMarkdown() },
|
|
23
|
+
{ filename: ".gaslighting/product/PRD.md", content: generatePrdMarkdown(input, analysis) },
|
|
24
|
+
{ filename: ".gaslighting/product/USER_FLOWS.md", content: userFlowsMarkdown(analysis, pages) },
|
|
25
|
+
{ filename: ".gaslighting/product/PAGE_MAP.md", content: pageMapMarkdown(pages) },
|
|
26
|
+
{ filename: ".gaslighting/features/core-scope/FEATURE.md", content: featureMarkdown(input, analysis) },
|
|
27
|
+
{ filename: ".gaslighting/features/core-scope/TASKS.md", content: tasksMarkdown(input, analysis, primaryPages) },
|
|
28
|
+
{ filename: ".gaslighting/features/core-scope/DECISIONS.md", content: featureDecisionsMarkdown(analysis) },
|
|
29
|
+
{ filename: ".gaslighting/features/core-scope/RISKS.md", content: featureRisksMarkdown(analysis) },
|
|
30
|
+
...primaryPages.flatMap((page) => pageDocs(page, analysis)),
|
|
31
|
+
{ filename: ".gaslighting/operations/GIT.md", content: operationGitMarkdown(root) },
|
|
32
|
+
{ filename: ".gaslighting/operations/GITHUB.md", content: operationGitHubMarkdown(root) },
|
|
33
|
+
{ filename: ".gaslighting/operations/DOMAIN.md", content: operationDomainMarkdown() },
|
|
34
|
+
{ filename: ".gaslighting/operations/DEPLOYMENT.md", content: operationDeploymentMarkdown() },
|
|
35
|
+
{ filename: ".gaslighting/operations/ENV.md", content: operationEnvMarkdown() },
|
|
36
|
+
{ filename: ".gaslighting/decisions/ADR-0001-stack.md", content: adrStackMarkdown(analysis) },
|
|
37
|
+
{ filename: ".gaslighting/decisions/ADR-0002-agent-runtime.md", content: adrAgentRuntimeMarkdown(input) },
|
|
38
|
+
{ filename: ".gaslighting/tasks/TASK-0001.md", content: firstTaskMarkdown(input, analysis) },
|
|
39
|
+
{ filename: ".gaslighting/loops/current-session.json", content: loopSessionJson(input) },
|
|
40
|
+
{ filename: ".gaslighting/checkpoints/latest.md", content: checkpointMarkdown(input) },
|
|
41
|
+
{ filename: ".gaslighting/verification/latest-report.md", content: verificationReportMarkdown() },
|
|
42
|
+
{ filename: ".gaslighting/memory/PROJECT_MEMORY.md", content: projectMemoryMarkdown(input, analysis) },
|
|
43
|
+
{ filename: ".gaslighting/memory/SESSION_LOG.md", content: sessionLogMarkdown(input) },
|
|
44
|
+
{ filename: ".gaslighting/memory/OPEN_QUESTIONS.md", content: openQuestionsMarkdown(analysis) },
|
|
45
|
+
{ filename: ".gaslighting/operations/PROJECT_CARE.md", content: generateProjectCareMarkdown(input, analysis, root) },
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
function gaslightingConfigJson(input) {
|
|
49
|
+
return `${JSON.stringify({
|
|
50
|
+
defaultRuntime: input.runtime ?? "codex",
|
|
51
|
+
loopMode: input.loopMode ?? "off",
|
|
52
|
+
permissionMode: input.permissionMode ?? "auto_review",
|
|
53
|
+
skillSource: ".agents",
|
|
54
|
+
documentLayout: "structured",
|
|
55
|
+
}, null, 2)}\n`;
|
|
56
|
+
}
|
|
57
|
+
function agentsConfigJson(input) {
|
|
58
|
+
return `${JSON.stringify({
|
|
59
|
+
sourceOfTruth: true,
|
|
60
|
+
defaultRuntime: input.runtime ?? "codex",
|
|
61
|
+
permissionMode: input.permissionMode ?? "auto_review",
|
|
62
|
+
exports: [".codex"],
|
|
63
|
+
skillRoot: ".agents/skills",
|
|
64
|
+
mcpRoot: ".agents/mcp",
|
|
65
|
+
}, null, 2)}\n`;
|
|
66
|
+
}
|
|
67
|
+
function runtimeConfig(id, supportsMcp, supportsSkills) {
|
|
68
|
+
return `${JSON.stringify({ id, enabled: id === "codex", supportsMcp, supportsSkills }, null, 2)}\n`;
|
|
69
|
+
}
|
|
70
|
+
function mcpManifestJson() {
|
|
71
|
+
return `${JSON.stringify({ servers: [], installedAt: null, source: ".agents/mcp" }, null, 2)}\n`;
|
|
72
|
+
}
|
|
73
|
+
function indexMarkdown(input, analysis, pages) {
|
|
74
|
+
return `# Gaslighting-engine Index
|
|
75
|
+
|
|
76
|
+
This is the structured source of truth for the project discipline layer.
|
|
77
|
+
|
|
78
|
+
## Mission
|
|
79
|
+
|
|
80
|
+
- Original request: ${input.rawUserRequest}
|
|
81
|
+
- Project type: ${analysis.projectType}
|
|
82
|
+
- Classification confidence: ${analysis.confidence}
|
|
83
|
+
- Default runtime: ${input.runtime ?? "codex"}
|
|
84
|
+
- Loop mode: ${input.loopMode ?? "off"}
|
|
85
|
+
- Permission mode: ${input.permissionMode ?? "auto_review"}
|
|
86
|
+
|
|
87
|
+
## Read Order For Agents
|
|
88
|
+
|
|
89
|
+
1. \`mission/PROJECT_PURPOSE.md\`
|
|
90
|
+
2. \`mission/EXECUTION_RULES.md\`
|
|
91
|
+
3. \`mission/COMPLETION_STANDARD.md\`
|
|
92
|
+
4. \`product/PRD.md\`
|
|
93
|
+
5. \`product/PAGE_MAP.md\`
|
|
94
|
+
6. \`features/core-scope/TASKS.md\`
|
|
95
|
+
7. \`operations/PROJECT_CARE.md\`
|
|
96
|
+
8. \`loops/current-session.json\`
|
|
97
|
+
|
|
98
|
+
## Pages
|
|
99
|
+
|
|
100
|
+
${pages.map((page) => `- ${page}`).join("\n")}
|
|
101
|
+
|
|
102
|
+
## Compatibility Files
|
|
103
|
+
|
|
104
|
+
Older agents may still read the root compatibility markdown files in \`.gaslighting/\`. Those files remain generated for backward compatibility, but this index is the preferred entrypoint.
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
function projectPurposeMarkdown(input, analysis, purpose) {
|
|
108
|
+
return `# Project Purpose
|
|
109
|
+
|
|
110
|
+
Original request:
|
|
111
|
+
|
|
112
|
+
> ${input.rawUserRequest}
|
|
113
|
+
|
|
114
|
+
Project type: \`${analysis.projectType}\`
|
|
115
|
+
|
|
116
|
+
The purpose is:
|
|
117
|
+
|
|
118
|
+
${purpose.map((item) => `- ${item}`).join("\n")}
|
|
119
|
+
|
|
120
|
+
Do not turn this project into a generic showcase. Preserve the business purpose and conversion/operation goals implied by the project type.
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
function executionRulesMarkdown(input) {
|
|
124
|
+
return `# Execution Rules
|
|
125
|
+
|
|
126
|
+
- Runtime: ${input.runtime ?? "codex"}
|
|
127
|
+
- Loop mode: ${input.loopMode ?? "off"}
|
|
128
|
+
- Permission mode: ${input.permissionMode ?? "auto_review"}
|
|
129
|
+
|
|
130
|
+
## Non-Negotiables
|
|
131
|
+
|
|
132
|
+
- Do the actual work.
|
|
133
|
+
- Do not shrink scope.
|
|
134
|
+
- Do not use TODOs as fake implementation.
|
|
135
|
+
- Do not present representative examples as full coverage.
|
|
136
|
+
- Document missing information and continue unless truly blocked.
|
|
137
|
+
- Record operational risk in \`operations/PROJECT_CARE.md\`.
|
|
138
|
+
|
|
139
|
+
## Permission Modes
|
|
140
|
+
|
|
141
|
+
- \`ask_each_task\`: request approval before each task action.
|
|
142
|
+
- \`auto_review\`: continue ordinary work; request approval for destructive, publishing, deployment, secret, DNS, payment, MCP install, or global configuration changes.
|
|
143
|
+
- \`full_autonomy\`: continue without approval, but log every action and never expose secrets.
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
function completionStandardMarkdown() {
|
|
147
|
+
return `# Completion Standard
|
|
148
|
+
|
|
149
|
+
A task is complete only when:
|
|
150
|
+
|
|
151
|
+
- Requested scope is fully handled.
|
|
152
|
+
- No required item was silently skipped.
|
|
153
|
+
- No placeholder is pretending to be implementation.
|
|
154
|
+
- No TODO replaces requested implementation.
|
|
155
|
+
- Verification was run or the reason it could not run is stated.
|
|
156
|
+
- Assumptions, missing information, and operational care risks are updated.
|
|
157
|
+
- The result can actually be used.
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
function userFlowsMarkdown(analysis, pages) {
|
|
161
|
+
return `# User Flows
|
|
162
|
+
|
|
163
|
+
Project type: \`${analysis.projectType}\`
|
|
164
|
+
|
|
165
|
+
## Primary Flow
|
|
166
|
+
|
|
167
|
+
1. User lands on the first relevant page.
|
|
168
|
+
2. User understands the offer or purpose quickly.
|
|
169
|
+
3. User explores the page or feature set needed for trust and decision-making.
|
|
170
|
+
4. User completes the primary conversion or operational action.
|
|
171
|
+
|
|
172
|
+
## Page Coverage
|
|
173
|
+
|
|
174
|
+
${pages.map((page, index) => `${index + 1}. ${page}`).join("\n")}
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
function pageMapMarkdown(pages) {
|
|
178
|
+
return `# Page Map
|
|
179
|
+
|
|
180
|
+
${pages.map((page) => `- [ ] ${page}: define purpose, content, CTA, empty state, and acceptance criteria.`).join("\n")}
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
183
|
+
function featureMarkdown(input, analysis) {
|
|
184
|
+
return `# Core Scope Feature
|
|
185
|
+
|
|
186
|
+
Original request: ${input.rawUserRequest}
|
|
187
|
+
|
|
188
|
+
This feature captures the complete MVP scope for \`${analysis.projectType}\`.
|
|
189
|
+
|
|
190
|
+
The agent must not reduce it to a sample, scaffold, or visual-only shell.
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
function tasksMarkdown(input, analysis, pages) {
|
|
194
|
+
return `# Core Scope Tasks
|
|
195
|
+
|
|
196
|
+
- [ ] Preserve and restate the project purpose for \`${analysis.projectType}\`.
|
|
197
|
+
- [ ] Implement all required MVP pages or feature surfaces.
|
|
198
|
+
${pages.map((page) => `- [ ] Implement and verify page/surface: ${page}`).join("\n")}
|
|
199
|
+
- [ ] Verify build/test/lint/browser flow as appropriate.
|
|
200
|
+
- [ ] Update decisions, missing info, memory, and project care files.
|
|
201
|
+
|
|
202
|
+
Original request:
|
|
203
|
+
|
|
204
|
+
> ${input.rawUserRequest}
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
function featureDecisionsMarkdown(analysis) {
|
|
208
|
+
return `# Feature Decisions
|
|
209
|
+
|
|
210
|
+
- Project type initialized as \`${analysis.projectType}\`.
|
|
211
|
+
- Deterministic template generation is used instead of an LLM API.
|
|
212
|
+
- Scope preservation is mandatory.
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
function featureRisksMarkdown(analysis) {
|
|
216
|
+
return `# Feature Risks
|
|
217
|
+
|
|
218
|
+
- Classification confidence: ${analysis.confidence}
|
|
219
|
+
- Missing information can affect copy, data model, page details, and production readiness.
|
|
220
|
+
- Repetitive surfaces are at risk of being summarized instead of implemented.
|
|
221
|
+
`;
|
|
222
|
+
}
|
|
223
|
+
function pageDocs(page, analysis) {
|
|
224
|
+
const slug = slugify(page);
|
|
225
|
+
return [
|
|
226
|
+
{
|
|
227
|
+
filename: `.gaslighting/pages/${slug}/PAGE_PRD.md`,
|
|
228
|
+
content: `# ${page} Page PRD
|
|
229
|
+
|
|
230
|
+
Project type: \`${analysis.projectType}\`
|
|
231
|
+
|
|
232
|
+
## Purpose
|
|
233
|
+
|
|
234
|
+
This page must support the project purpose and not become decorative filler.
|
|
235
|
+
|
|
236
|
+
## Required Coverage
|
|
237
|
+
|
|
238
|
+
- Clear user goal.
|
|
239
|
+
- Clear content hierarchy.
|
|
240
|
+
- Clear CTA or operational action.
|
|
241
|
+
- Mobile usability.
|
|
242
|
+
- Empty/error states when relevant.
|
|
243
|
+
`,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
filename: `.gaslighting/pages/${slug}/ACCEPTANCE.md`,
|
|
247
|
+
content: `# ${page} Acceptance Criteria
|
|
248
|
+
|
|
249
|
+
- [ ] Page purpose is visible.
|
|
250
|
+
- [ ] Required content is not placeholder-only.
|
|
251
|
+
- [ ] CTA or operational action is usable.
|
|
252
|
+
- [ ] Mobile layout is usable.
|
|
253
|
+
- [ ] No TODO replaces implementation.
|
|
254
|
+
`,
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
function operationGitMarkdown(root) {
|
|
259
|
+
return `# Git Operations
|
|
260
|
+
|
|
261
|
+
Project root: \`${root}\`
|
|
262
|
+
|
|
263
|
+
- Keep meaningful checkpoints.
|
|
264
|
+
- Do not rewrite user changes.
|
|
265
|
+
- Record remote changes in decisions.
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
function operationGitHubMarkdown(root) {
|
|
269
|
+
return `# GitHub Operations
|
|
270
|
+
|
|
271
|
+
Project root: \`${root}\`
|
|
272
|
+
|
|
273
|
+
- If no GitHub remote exists, ask for the GitHub URL and offer to connect it.
|
|
274
|
+
- If a remote exists, preserve it unless the user explicitly asks to replace it.
|
|
275
|
+
- Push only when permission mode allows it or approval is granted.
|
|
276
|
+
`;
|
|
277
|
+
}
|
|
278
|
+
function operationDomainMarkdown() {
|
|
279
|
+
return `# Domain Operations
|
|
280
|
+
|
|
281
|
+
- Production domain: missing until confirmed.
|
|
282
|
+
- DNS provider: missing until confirmed.
|
|
283
|
+
- Domain owner: missing until confirmed.
|
|
284
|
+
- Do not treat missing domain data as implementation-blocking unless launch is requested.
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
function operationDeploymentMarkdown() {
|
|
288
|
+
return `# Deployment Operations
|
|
289
|
+
|
|
290
|
+
- Deployment provider: unconfirmed until detected or selected.
|
|
291
|
+
- Preview URL policy: missing until confirmed.
|
|
292
|
+
- Rollback path: missing until confirmed.
|
|
293
|
+
- Deployment commands require approval in auto-review mode.
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
function operationEnvMarkdown() {
|
|
297
|
+
return `# Environment Operations
|
|
298
|
+
|
|
299
|
+
- Keep real secrets out of generated documents.
|
|
300
|
+
- Prefer \`.env.example\` for required variables.
|
|
301
|
+
- Secret changes require approval in auto-review mode.
|
|
302
|
+
`;
|
|
303
|
+
}
|
|
304
|
+
function adrStackMarkdown(analysis) {
|
|
305
|
+
return `# ADR-0001: Stack Policy
|
|
306
|
+
|
|
307
|
+
Date: ${decisionDate()}
|
|
308
|
+
|
|
309
|
+
## Decision
|
|
310
|
+
|
|
311
|
+
${stackSummary(analysis)}
|
|
312
|
+
|
|
313
|
+
## Consequence
|
|
314
|
+
|
|
315
|
+
The agent must avoid over-engineering and choose technology that serves the project purpose.
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
function adrAgentRuntimeMarkdown(input) {
|
|
319
|
+
return `# ADR-0002: Agent Runtime
|
|
320
|
+
|
|
321
|
+
Date: ${decisionDate()}
|
|
322
|
+
|
|
323
|
+
## Decision
|
|
324
|
+
|
|
325
|
+
Default runtime is \`${input.runtime ?? "codex"}\`.
|
|
326
|
+
|
|
327
|
+
Loop mode defaults to \`${input.loopMode ?? "off"}\`.
|
|
328
|
+
|
|
329
|
+
Permission mode defaults to \`${input.permissionMode ?? "auto_review"}\`.
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
function firstTaskMarkdown(input, analysis) {
|
|
333
|
+
return `# TASK-0001: Initialize Mission Scope
|
|
334
|
+
|
|
335
|
+
Status: pending
|
|
336
|
+
Runtime: ${input.runtime ?? "codex"}
|
|
337
|
+
Permission mode: ${input.permissionMode ?? "auto_review"}
|
|
338
|
+
|
|
339
|
+
## Goal
|
|
340
|
+
|
|
341
|
+
Turn the original request into implemented project work without fake completion.
|
|
342
|
+
|
|
343
|
+
## Request
|
|
344
|
+
|
|
345
|
+
> ${input.rawUserRequest}
|
|
346
|
+
|
|
347
|
+
## Project Type
|
|
348
|
+
|
|
349
|
+
\`${analysis.projectType}\`
|
|
350
|
+
`;
|
|
351
|
+
}
|
|
352
|
+
function loopSessionJson(input) {
|
|
353
|
+
return `${JSON.stringify({
|
|
354
|
+
active: input.loopMode === "on",
|
|
355
|
+
status: input.loopMode === "on" ? "ready" : "off",
|
|
356
|
+
currentTask: "TASK-0001",
|
|
357
|
+
runtime: input.runtime ?? "codex",
|
|
358
|
+
permissionMode: input.permissionMode ?? "auto_review",
|
|
359
|
+
iterations: 0,
|
|
360
|
+
stopRequested: false,
|
|
361
|
+
}, null, 2)}\n`;
|
|
362
|
+
}
|
|
363
|
+
function checkpointMarkdown(input) {
|
|
364
|
+
return `# Latest Checkpoint
|
|
365
|
+
|
|
366
|
+
No loop iteration has completed yet.
|
|
367
|
+
|
|
368
|
+
Original request:
|
|
369
|
+
|
|
370
|
+
> ${input.rawUserRequest}
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
function verificationReportMarkdown() {
|
|
374
|
+
return `# Latest Verification Report
|
|
375
|
+
|
|
376
|
+
No verification has run yet.
|
|
377
|
+
`;
|
|
378
|
+
}
|
|
379
|
+
function projectMemoryMarkdown(input, analysis) {
|
|
380
|
+
return `# Project Memory
|
|
381
|
+
|
|
382
|
+
- Original request: ${input.rawUserRequest}
|
|
383
|
+
- Project type: ${analysis.projectType}
|
|
384
|
+
- Classification confidence: ${analysis.confidence}
|
|
385
|
+
`;
|
|
386
|
+
}
|
|
387
|
+
function sessionLogMarkdown(input) {
|
|
388
|
+
return `# Session Log
|
|
389
|
+
|
|
390
|
+
- ${decisionDate()}: Gaslighting-engine structured mission initialized for: ${input.rawUserRequest}
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
function openQuestionsMarkdown(analysis) {
|
|
394
|
+
return `# Open Questions
|
|
395
|
+
|
|
396
|
+
${analysis.missingInfo.map((item) => `- ${item.item}: ${item.recommendedAction}`).join("\n") || "- No open questions recorded yet."}
|
|
397
|
+
`;
|
|
398
|
+
}
|
|
399
|
+
function slugify(value) {
|
|
400
|
+
return value
|
|
401
|
+
.toLowerCase()
|
|
402
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
403
|
+
.replace(/^-+|-+$/g, "")
|
|
404
|
+
.slice(0, 64) || "page";
|
|
405
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
const registry = [
|
|
4
|
+
{
|
|
5
|
+
id: "github",
|
|
6
|
+
label: "GitHub",
|
|
7
|
+
description: "Repository, issue, pull request, and code review operations.",
|
|
8
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
9
|
+
runtimes: ["codex", "claude", "qwen", "opencode"],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: "vercel",
|
|
13
|
+
label: "Vercel",
|
|
14
|
+
description: "Deployments, domains, logs, projects, and environment variables.",
|
|
15
|
+
url: "https://mcp.vercel.com",
|
|
16
|
+
runtimes: ["codex", "claude", "qwen", "opencode"],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "postgres",
|
|
20
|
+
label: "PostgreSQL",
|
|
21
|
+
description: "Database schema and readonly query workflows through dbhub.",
|
|
22
|
+
command: "npx",
|
|
23
|
+
args: ["-y", "@bytebase/dbhub"],
|
|
24
|
+
runtimes: ["codex", "claude", "qwen", "opencode"],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "figma",
|
|
28
|
+
label: "Figma",
|
|
29
|
+
description: "Design context and component handoff workflows.",
|
|
30
|
+
url: "https://mcp.figma.com/mcp",
|
|
31
|
+
runtimes: ["codex", "claude", "qwen", "opencode"],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
export function searchMcpServers(query) {
|
|
35
|
+
const normalized = query.toLowerCase();
|
|
36
|
+
return registry.filter((server) => `${server.id} ${server.label} ${server.description}`.toLowerCase().includes(normalized));
|
|
37
|
+
}
|
|
38
|
+
export function listInstalledMcpServers(root) {
|
|
39
|
+
const manifest = readManifest(root);
|
|
40
|
+
return manifest.servers;
|
|
41
|
+
}
|
|
42
|
+
export function installMcpServer(root, id, runtime) {
|
|
43
|
+
const server = registry.find((item) => item.id === id);
|
|
44
|
+
if (!server)
|
|
45
|
+
return { ok: false, message: `Unknown MCP server: ${id}` };
|
|
46
|
+
const manifest = readManifest(root);
|
|
47
|
+
manifest.servers = [...manifest.servers.filter((item) => item.id !== server.id), server];
|
|
48
|
+
writeJson(join(root, ".agents", "mcp", "manifest.json"), manifest);
|
|
49
|
+
exportMcp(root, server, runtime);
|
|
50
|
+
return { ok: true, message: `Installed ${server.label} MCP definition for ${runtime}.` };
|
|
51
|
+
}
|
|
52
|
+
export function doctorMcp(root) {
|
|
53
|
+
const installed = listInstalledMcpServers(root);
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
ok: existsSync(join(root, ".agents", "mcp", "manifest.json")),
|
|
57
|
+
message: ".agents/mcp/manifest.json exists.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
ok: installed.length > 0,
|
|
61
|
+
message: installed.length > 0 ? `${installed.length} MCP server definition(s) installed.` : "No MCP servers installed yet.",
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
function exportMcp(root, server, runtime) {
|
|
66
|
+
if (runtime === "codex") {
|
|
67
|
+
const path = join(root, ".codex", "config.toml");
|
|
68
|
+
const body = server.url
|
|
69
|
+
? `[mcp_servers.${server.id}]\nurl = "${server.url}"\n`
|
|
70
|
+
: `[mcp_servers.${server.id}]\ncommand = "${server.command}"\nargs = [${(server.args ?? []).map((arg) => `"${arg}"`).join(", ")}]\n`;
|
|
71
|
+
appendUnique(path, body, `[mcp_servers.${server.id}]`);
|
|
72
|
+
}
|
|
73
|
+
if (runtime === "claude") {
|
|
74
|
+
const path = join(root, ".mcp.json");
|
|
75
|
+
const existing = existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : { mcpServers: {} };
|
|
76
|
+
existing.mcpServers[server.id] = server.url ? { type: "http", url: server.url } : { command: server.command, args: server.args ?? [] };
|
|
77
|
+
writeJson(path, existing);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function readManifest(root) {
|
|
81
|
+
const path = join(root, ".agents", "mcp", "manifest.json");
|
|
82
|
+
if (!existsSync(path))
|
|
83
|
+
return { servers: [] };
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { servers: [] };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function appendUnique(path, body, marker) {
|
|
92
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
93
|
+
if (existing.includes(marker))
|
|
94
|
+
return;
|
|
95
|
+
writeText(path, `${existing.trimEnd()}\n\n${body}`.trimStart());
|
|
96
|
+
}
|
|
97
|
+
function writeJson(path, data) {
|
|
98
|
+
writeText(path, `${JSON.stringify(data, null, 2)}\n`);
|
|
99
|
+
}
|
|
100
|
+
function writeText(path, content) {
|
|
101
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
102
|
+
writeFileSync(path, content, "utf8");
|
|
103
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { getAgentRuntimeAdapter } from "../agent-runtimes/registry.js";
|
|
5
|
+
import { reviewPermission } from "../agent-runtimes/permissions.js";
|
|
6
|
+
import { generateDocs } from "./generateDocs.js";
|
|
7
|
+
import { writeDocs } from "../utils/file.js";
|
|
8
|
+
import { decisionDate } from "./content.js";
|
|
9
|
+
const sessionFile = ".gaslighting/loops/current-session.json";
|
|
10
|
+
export function startMissionLoop(options) {
|
|
11
|
+
const request = options.request || "Continue the current Gaslighting mission without shrinking scope.";
|
|
12
|
+
const input = {
|
|
13
|
+
rawUserRequest: request,
|
|
14
|
+
runtime: options.runtime ?? "codex",
|
|
15
|
+
permissionMode: options.permissionMode ?? "auto_review",
|
|
16
|
+
loopMode: "on",
|
|
17
|
+
mode: "hardcore",
|
|
18
|
+
fullScope: true,
|
|
19
|
+
noTodo: true,
|
|
20
|
+
noShortcut: true,
|
|
21
|
+
force: true,
|
|
22
|
+
};
|
|
23
|
+
writeDocs(options.root, generateDocs(input, options.root).docs, true, false);
|
|
24
|
+
const session = {
|
|
25
|
+
active: true,
|
|
26
|
+
status: "ready",
|
|
27
|
+
request,
|
|
28
|
+
currentTask: "TASK-0001",
|
|
29
|
+
runtime: input.runtime ?? "codex",
|
|
30
|
+
permissionMode: input.permissionMode ?? "auto_review",
|
|
31
|
+
loopMode: "on",
|
|
32
|
+
iterations: 0,
|
|
33
|
+
retryCount: 0,
|
|
34
|
+
stopRequested: false,
|
|
35
|
+
lastMessage: "Mission loop started.",
|
|
36
|
+
};
|
|
37
|
+
saveLoopSession(options.root, session);
|
|
38
|
+
return session;
|
|
39
|
+
}
|
|
40
|
+
export function getMissionLoopStatus(root) {
|
|
41
|
+
return readLoopSession(root);
|
|
42
|
+
}
|
|
43
|
+
export function stopMissionLoop(root) {
|
|
44
|
+
const session = readLoopSession(root) ?? defaultStoppedSession();
|
|
45
|
+
session.active = false;
|
|
46
|
+
session.stopRequested = true;
|
|
47
|
+
session.status = "stopped";
|
|
48
|
+
session.lastMessage = "Mission loop stop requested.";
|
|
49
|
+
saveLoopSession(root, session);
|
|
50
|
+
appendSessionLog(root, "Mission loop stopped.");
|
|
51
|
+
return session;
|
|
52
|
+
}
|
|
53
|
+
export function resumeMissionLoop(options) {
|
|
54
|
+
let session = readLoopSession(options.root);
|
|
55
|
+
if (!session) {
|
|
56
|
+
session = startMissionLoop({ ...options, request: options.request || "Continue the current Gaslighting mission." });
|
|
57
|
+
}
|
|
58
|
+
if (session.stopRequested) {
|
|
59
|
+
session.stopRequested = false;
|
|
60
|
+
session.active = true;
|
|
61
|
+
session.status = "ready";
|
|
62
|
+
}
|
|
63
|
+
const maxIterations = Math.max(1, options.maxIterations ?? 1);
|
|
64
|
+
for (let index = 0; index < maxIterations; index += 1) {
|
|
65
|
+
if (!session.active || session.stopRequested)
|
|
66
|
+
break;
|
|
67
|
+
session = runOneIteration(options.root, session, Boolean(options.approve), options.extraArgs);
|
|
68
|
+
if (session.status === "approval_required" || session.status === "failed")
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
saveLoopSession(options.root, session);
|
|
72
|
+
return session;
|
|
73
|
+
}
|
|
74
|
+
function runOneIteration(root, session, approved, extraArgs) {
|
|
75
|
+
const adapter = getAgentRuntimeAdapter(session.runtime);
|
|
76
|
+
const action = adapter.buildCommand({
|
|
77
|
+
root,
|
|
78
|
+
request: `${session.request}\n\nWork only on ${session.currentTask}. Update Gaslighting loop state honestly.`,
|
|
79
|
+
runtime: session.runtime,
|
|
80
|
+
permissionMode: session.permissionMode,
|
|
81
|
+
loopMode: "on",
|
|
82
|
+
extraArgs,
|
|
83
|
+
});
|
|
84
|
+
const review = reviewPermission(session.permissionMode, action);
|
|
85
|
+
if (review.requiresApproval && !approved) {
|
|
86
|
+
return {
|
|
87
|
+
...session,
|
|
88
|
+
status: "approval_required",
|
|
89
|
+
lastMessage: review.reason,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const spawnInput = adapter.buildSpawn({
|
|
93
|
+
root,
|
|
94
|
+
request: `${session.request}\n\nWork only on ${session.currentTask}. Update Gaslighting loop state honestly.`,
|
|
95
|
+
runtime: session.runtime,
|
|
96
|
+
permissionMode: session.permissionMode,
|
|
97
|
+
loopMode: "on",
|
|
98
|
+
extraArgs,
|
|
99
|
+
});
|
|
100
|
+
saveLoopSession(root, { ...session, status: "running", lastMessage: `Running ${session.currentTask} with ${adapter.label}.` });
|
|
101
|
+
const startedAt = new Date().toISOString();
|
|
102
|
+
const result = spawnSync(spawnInput.file, spawnInput.args, { cwd: spawnInput.cwd, encoding: "utf8", stdio: "pipe" });
|
|
103
|
+
const endedAt = new Date().toISOString();
|
|
104
|
+
const ok = result.status === 0;
|
|
105
|
+
const nextSession = {
|
|
106
|
+
...session,
|
|
107
|
+
iterations: session.iterations + 1,
|
|
108
|
+
retryCount: ok ? 0 : session.retryCount + 1,
|
|
109
|
+
status: ok ? "ready" : "failed",
|
|
110
|
+
lastMessage: ok ? `${session.currentTask} iteration completed. Verification still required before claiming done.` : `${session.currentTask} iteration failed.`,
|
|
111
|
+
};
|
|
112
|
+
writeIteration(root, nextSession.iterations, {
|
|
113
|
+
task: session.currentTask,
|
|
114
|
+
runtime: session.runtime,
|
|
115
|
+
command: action,
|
|
116
|
+
startedAt,
|
|
117
|
+
endedAt,
|
|
118
|
+
exitCode: result.status,
|
|
119
|
+
stdout: redact(result.stdout ?? ""),
|
|
120
|
+
stderr: redact(result.stderr ?? ""),
|
|
121
|
+
});
|
|
122
|
+
writeVerification(root, ok, nextSession.lastMessage ?? "");
|
|
123
|
+
appendSessionLog(root, nextSession.lastMessage ?? "Loop iteration completed.");
|
|
124
|
+
return nextSession;
|
|
125
|
+
}
|
|
126
|
+
function readLoopSession(root) {
|
|
127
|
+
const path = join(root, sessionFile);
|
|
128
|
+
if (!existsSync(path))
|
|
129
|
+
return undefined;
|
|
130
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
131
|
+
}
|
|
132
|
+
function saveLoopSession(root, session) {
|
|
133
|
+
writeJson(join(root, sessionFile), session);
|
|
134
|
+
}
|
|
135
|
+
function writeIteration(root, iteration, data) {
|
|
136
|
+
writeJson(join(root, ".gaslighting", "loops", "iterations", `${String(iteration).padStart(4, "0")}.json`), data);
|
|
137
|
+
}
|
|
138
|
+
function writeVerification(root, ok, message) {
|
|
139
|
+
writeText(join(root, ".gaslighting", "verification", "latest-report.md"), `# Latest Verification Report
|
|
140
|
+
|
|
141
|
+
- Date: ${decisionDate()}
|
|
142
|
+
- Status: ${ok ? "pass" : "fail"}
|
|
143
|
+
- Message: ${message}
|
|
144
|
+
|
|
145
|
+
Loop verification confirms process exit only. The agent must still run project-specific build, test, lint, and browser checks before declaring final completion.
|
|
146
|
+
`);
|
|
147
|
+
}
|
|
148
|
+
function appendSessionLog(root, message) {
|
|
149
|
+
const path = join(root, ".gaslighting", "memory", "SESSION_LOG.md");
|
|
150
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : "# Session Log\n";
|
|
151
|
+
writeText(path, `${existing.trimEnd()}\n- ${decisionDate()}: ${message}\n`);
|
|
152
|
+
}
|
|
153
|
+
function writeJson(path, data) {
|
|
154
|
+
writeText(path, `${JSON.stringify(data, null, 2)}\n`);
|
|
155
|
+
}
|
|
156
|
+
function writeText(path, content) {
|
|
157
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
158
|
+
writeFileSync(path, content, "utf8");
|
|
159
|
+
}
|
|
160
|
+
function defaultStoppedSession() {
|
|
161
|
+
return {
|
|
162
|
+
active: false,
|
|
163
|
+
status: "stopped",
|
|
164
|
+
request: "No loop session exists.",
|
|
165
|
+
currentTask: "TASK-0001",
|
|
166
|
+
runtime: "codex",
|
|
167
|
+
permissionMode: "auto_review",
|
|
168
|
+
loopMode: "off",
|
|
169
|
+
iterations: 0,
|
|
170
|
+
retryCount: 0,
|
|
171
|
+
stopRequested: true,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function redact(value) {
|
|
175
|
+
return value
|
|
176
|
+
.replace(/npm_[A-Za-z0-9]+/g, "[REDACTED_NPM_TOKEN]")
|
|
177
|
+
.replace(/sk-[A-Za-z0-9_-]+/g, "[REDACTED_API_KEY]")
|
|
178
|
+
.slice(0, 20_000);
|
|
179
|
+
}
|