opencode-orchestrator-plugin 1.0.0-beta.1 → 1.0.0-beta.11
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 +35 -2
- package/dist/index.js +30 -78
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +122 -0
- package/dist/prompts/agent/orchestrator.md +7 -1
- package/dist/prompts/orchestrator/setup.json +2 -2
- package/dist/prompts/orchestrator/setup.test.d.ts +1 -0
- package/dist/prompts/orchestrator/setup.test.js +132 -0
- package/dist/prompts/orchestrator/setup.test.ts +168 -0
- package/dist/utils/autogenerateFlow.d.ts +37 -0
- package/dist/utils/autogenerateFlow.js +218 -0
- package/dist/utils/autogenerateFlow.test.d.ts +1 -0
- package/dist/utils/autogenerateFlow.test.js +337 -0
- package/dist/utils/configDetection.d.ts +6 -0
- package/dist/utils/configDetection.js +35 -0
- package/dist/utils/configDetection.test.d.ts +1 -0
- package/dist/utils/configDetection.test.js +74 -0
- package/dist/utils/contentGeneration.d.ts +10 -0
- package/dist/utils/contentGeneration.js +141 -0
- package/dist/utils/contentGeneration.test.d.ts +1 -0
- package/dist/utils/contentGeneration.test.js +147 -0
- package/dist/utils/contextAnalysis.d.ts +100 -0
- package/dist/utils/contextAnalysis.js +308 -0
- package/dist/utils/contextAnalysis.test.d.ts +1 -0
- package/dist/utils/contextAnalysis.test.js +309 -0
- package/dist/utils/ignoreMatcher.d.ts +9 -0
- package/dist/utils/ignoreMatcher.js +77 -0
- package/dist/utils/ignoreMatcher.test.d.ts +1 -0
- package/dist/utils/ignoreMatcher.test.js +126 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,9 +121,42 @@ This project follows **Conventional Commits**. Releases are automated via Semant
|
|
|
121
121
|
|
|
122
122
|
---
|
|
123
123
|
|
|
124
|
-
## 🔁 Origin
|
|
124
|
+
## 🔁 Origin & Lineage
|
|
125
125
|
|
|
126
|
-
This project
|
|
126
|
+
This project is part of the Context-Driven Development lineage:
|
|
127
|
+
|
|
128
|
+
- **Original Source**: [gemini-cli-extensions/conductor](https://github.com/gemini-cli-extensions/conductor) - The original Gemini CLI framework
|
|
129
|
+
- **Direct Parent**: [derekbar90/opencode-conductor](https://github.com/derekbar90/opencode-conductor) - OpenCode port
|
|
130
|
+
- **This Project**: Evolution with new command and agent conventions for OpenCode v1.1.1+
|
|
131
|
+
|
|
132
|
+
## 🔄 Relationship to Conductor
|
|
133
|
+
|
|
134
|
+
Orchestrator brings the proven Context-Driven Development methodology from Gemini CLI to OpenCode with architectural adaptations:
|
|
135
|
+
|
|
136
|
+
### Architectural Differences
|
|
137
|
+
- **Command Format**: TOML-based Gemini CLI commands → JSON-based OpenCode plugin prompts
|
|
138
|
+
- **Execution Model**: Direct CLI invocation → Agent-based execution with `@orchestrator`
|
|
139
|
+
- **Permission System**: Gemini CLI native → OpenCode v1.1.1 granular permissions
|
|
140
|
+
|
|
141
|
+
### Feature Parity
|
|
142
|
+
See [Conductor Feature Parity Gap Analysis](./orchestrator/tracks/conductor_parity_20260118/gap-analysis.md) for a comprehensive comparison.
|
|
143
|
+
|
|
144
|
+
**Status**: ✅ **100% Feature Parity Achieved** (51/51 features)
|
|
145
|
+
|
|
146
|
+
Key highlights:
|
|
147
|
+
- ✅ All 5 core commands implemented
|
|
148
|
+
- ✅ Context → Spec → Plan → Implement lifecycle
|
|
149
|
+
- ✅ Documentation synchronization
|
|
150
|
+
- ✅ TDD workflow with Red-Green-Refactor
|
|
151
|
+
- ✅ Brownfield project support
|
|
152
|
+
- ✅ Autogenerate option for faster onboarding
|
|
153
|
+
- ✅ Phase completion verification with checkpoints
|
|
154
|
+
- ✅ Git notes integration for task tracking
|
|
155
|
+
|
|
156
|
+
### Orchestrator Enhancements
|
|
157
|
+
- 23 code styleguide templates (vs 8 in conductor)
|
|
158
|
+
- Native OpenCode plugin system integration
|
|
159
|
+
- Multi-agent workflow support (OhMyOpenCode synergy)
|
|
127
160
|
|
|
128
161
|
## 📜 License
|
|
129
162
|
|
package/dist/index.js
CHANGED
|
@@ -5,92 +5,35 @@ import NewTrackPromptJson from "./prompts/orchestrator/newTrack.json" with { typ
|
|
|
5
5
|
import RevertPromptJson from "./prompts/orchestrator/revert.json" with { type: "json" };
|
|
6
6
|
import SetupPromptJson from "./prompts/orchestrator/setup.json" with { type: "json" };
|
|
7
7
|
import StatusPromptJson from "./prompts/orchestrator/status.json" with { type: "json" };
|
|
8
|
+
import { detectOrchestratorConfig } from "./utils/configDetection.js";
|
|
8
9
|
const asPrompt = (prompt) => (typeof prompt === "string" ? prompt : "");
|
|
9
10
|
const asDescription = (description) => typeof description === "string" ? description : undefined;
|
|
10
11
|
export const MyPlugin = async ({ directory, }) => {
|
|
11
12
|
const orchestratorPath = path.join(directory, "orchestrator");
|
|
12
13
|
let fileHeirarchy = "";
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const ignoreFile = path.join(rootDir, ".ignore");
|
|
24
|
-
const geminiIgnoreFile = path.join(rootDir, ".geminiignore");
|
|
25
|
-
const gitIgnoreFile = path.join(rootDir, ".gitignore");
|
|
26
|
-
const gitIgnorePatterns = readIgnoreFile(gitIgnoreFile);
|
|
27
|
-
const ignoreOverrides = readIgnoreFile(ignoreFile)
|
|
28
|
-
.filter((pattern) => pattern.startsWith("!"))
|
|
29
|
-
.map((pattern) => pattern.slice(1));
|
|
30
|
-
const geminiIgnorePatterns = readIgnoreFile(geminiIgnoreFile);
|
|
31
|
-
const ignored = new Set();
|
|
32
|
-
for (const pattern of gitIgnorePatterns.concat(geminiIgnorePatterns)) {
|
|
33
|
-
if (!pattern.startsWith("#") && !pattern.startsWith("!")) {
|
|
34
|
-
ignored.add(pattern.replace(/\/$/, ""));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
const allowed = new Set(ignoreOverrides.map((pattern) => pattern.replace(/\/$/, "")));
|
|
38
|
-
const isIgnored = (relativePath) => {
|
|
39
|
-
const normalized = relativePath.replace(/\\/g, "/");
|
|
40
|
-
if (allowed.size > 0) {
|
|
41
|
-
for (const allow of allowed) {
|
|
42
|
-
if (normalized === allow || normalized.startsWith(`${allow}/`))
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
14
|
+
const getFilesRecursively = (dir) => {
|
|
15
|
+
let results = [];
|
|
16
|
+
if (!fs.existsSync(dir))
|
|
17
|
+
return results;
|
|
18
|
+
const list = fs.readdirSync(dir);
|
|
19
|
+
list.forEach((file) => {
|
|
20
|
+
const filePath = path.join(dir, file);
|
|
21
|
+
const stat = fs.statSync(filePath);
|
|
22
|
+
if (stat && stat.isDirectory()) {
|
|
23
|
+
results = results.concat(getFilesRecursively(filePath));
|
|
45
24
|
}
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
};
|
|
53
|
-
return { ignores: isIgnored };
|
|
54
|
-
};
|
|
55
|
-
const getFileTreeSummary = (rootDir, maxEntries = 200, maxChars = 4000) => {
|
|
56
|
-
const ig = buildIgnoreMatcher(rootDir);
|
|
57
|
-
const directories = new Set();
|
|
58
|
-
const queue = [rootDir];
|
|
59
|
-
while (queue.length > 0) {
|
|
60
|
-
const current = queue.shift();
|
|
61
|
-
if (!current)
|
|
62
|
-
continue;
|
|
63
|
-
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
-
const entryPath = path.join(current, entry.name);
|
|
66
|
-
const relativePath = path.relative(rootDir, entryPath) || ".";
|
|
67
|
-
if (ig.ignores(relativePath)) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (entry.isDirectory()) {
|
|
71
|
-
directories.add(path.relative(rootDir, entryPath) || ".");
|
|
72
|
-
queue.push(entryPath);
|
|
25
|
+
else {
|
|
26
|
+
if (filePath.endsWith(".json") || filePath.endsWith(".md")) {
|
|
27
|
+
results.push(filePath);
|
|
73
28
|
}
|
|
74
29
|
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let summary = "";
|
|
78
|
-
let count = 0;
|
|
79
|
-
for (const dir of sorted) {
|
|
80
|
-
if (summary.length >= maxChars || count >= maxEntries)
|
|
81
|
-
break;
|
|
82
|
-
summary += `${dir}\n`;
|
|
83
|
-
count += 1;
|
|
84
|
-
}
|
|
85
|
-
if (summary.length >= maxChars || count >= maxEntries) {
|
|
86
|
-
summary += "...\n";
|
|
87
|
-
}
|
|
88
|
-
return summary.trim();
|
|
30
|
+
});
|
|
31
|
+
return results;
|
|
89
32
|
};
|
|
90
33
|
if (fs.existsSync(orchestratorPath)) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
.map((
|
|
34
|
+
const files = getFilesRecursively(orchestratorPath);
|
|
35
|
+
fileHeirarchy = files
|
|
36
|
+
.map((f) => path.relative(directory, f))
|
|
94
37
|
.join("\n ");
|
|
95
38
|
}
|
|
96
39
|
const isOrchestratorSetup = () => {
|
|
@@ -98,15 +41,18 @@ export const MyPlugin = async ({ directory, }) => {
|
|
|
98
41
|
return fs.existsSync(setupStatePath);
|
|
99
42
|
};
|
|
100
43
|
const setupOccurred = isOrchestratorSetup();
|
|
44
|
+
const configDetection = detectOrchestratorConfig();
|
|
101
45
|
return {
|
|
102
46
|
config: async (_config) => {
|
|
103
47
|
_config.command = {
|
|
104
48
|
..._config.command,
|
|
105
49
|
"orchestrator:implement": {
|
|
50
|
+
agent: "orchestrator",
|
|
106
51
|
template: asPrompt(ImplementPromptJson.prompt) + `
|
|
107
52
|
Environment Details:
|
|
108
53
|
- Directory: ${directory}
|
|
109
54
|
- Orchestrator Setup: ${setupOccurred}
|
|
55
|
+
- Synergy Active (OMO): ${configDetection.synergyActive}
|
|
110
56
|
- Current Orchestrator Files (Location: ${directory}/orchestrator)
|
|
111
57
|
File Tree:
|
|
112
58
|
${fileHeirarchy}
|
|
@@ -114,33 +60,39 @@ export const MyPlugin = async ({ directory, }) => {
|
|
|
114
60
|
description: asDescription(ImplementPromptJson.description),
|
|
115
61
|
},
|
|
116
62
|
"orchestrator:newTrack": {
|
|
63
|
+
agent: "orchestrator",
|
|
117
64
|
template: asPrompt(NewTrackPromptJson.prompt),
|
|
118
65
|
description: asDescription(NewTrackPromptJson.description),
|
|
119
66
|
},
|
|
120
67
|
"orchestrator:revert": {
|
|
68
|
+
agent: "orchestrator",
|
|
121
69
|
template: asPrompt(RevertPromptJson.prompt),
|
|
122
70
|
description: asDescription(RevertPromptJson.description),
|
|
123
71
|
},
|
|
124
72
|
"orchestrator:setup": {
|
|
73
|
+
agent: "orchestrator",
|
|
125
74
|
template: asPrompt(SetupPromptJson.prompt) + `
|
|
126
75
|
Environment Details:
|
|
127
76
|
- Directory: ${directory}
|
|
128
77
|
- Orchestrator Setup: ${setupOccurred}
|
|
78
|
+
- Synergy Active (OMO): ${configDetection.synergyActive}
|
|
129
79
|
- Current Orchestrator Files (with tracks) (${directory}/orchestrator)
|
|
130
80
|
File Tree:
|
|
131
81
|
${fileHeirarchy}
|
|
132
|
-
|
|
82
|
+
|
|
133
83
|
**CRITICAL ENVIRONTMENTAL OVERRIDE:**: You are running inside OpenCode. Use ./config/opencode/node_modules/opencode-orchestrator-plugin for setup operations and respect .ignore (allowlist), .geminiignore, and .gitignore patterns when excluding files.
|
|
134
84
|
`,
|
|
135
85
|
description: asDescription(SetupPromptJson.description),
|
|
136
86
|
},
|
|
137
87
|
"orchestrator:status": {
|
|
88
|
+
agent: "orchestrator",
|
|
138
89
|
template: asPrompt(StatusPromptJson.prompt) + `
|
|
139
|
-
|
|
90
|
+
|
|
140
91
|
|
|
141
92
|
***Current Environment Details***:
|
|
142
93
|
- Current Working Directory: ${directory}
|
|
143
94
|
- Orchestrator Setup Process Completed: ${setupOccurred}
|
|
95
|
+
- Synergy Active (OMO): ${configDetection.synergyActive}
|
|
144
96
|
- Current Orchestrator Files (with tracks) (${directory}/orchestrator)
|
|
145
97
|
File Tree:
|
|
146
98
|
${fileHeirarchy}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { buildIgnoreMatcher } from "./utils/ignoreMatcher.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
describe("getFileTreeSummary behavior", () => {
|
|
6
|
+
const testDir = path.join(process.cwd(), "test-temp-orchestrator-tree");
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
if (fs.existsSync(testDir)) {
|
|
9
|
+
fs.rmSync(testDir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
if (fs.existsSync(testDir)) {
|
|
15
|
+
fs.rmSync(testDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
it("should exclude .git directory from tree", () => {
|
|
19
|
+
fs.mkdirSync(path.join(testDir, ".git"));
|
|
20
|
+
fs.mkdirSync(path.join(testDir, "src"));
|
|
21
|
+
const ig = buildIgnoreMatcher(testDir, {
|
|
22
|
+
gitignore: [],
|
|
23
|
+
ignore: [],
|
|
24
|
+
geminiignore: [],
|
|
25
|
+
});
|
|
26
|
+
const directories = new Set();
|
|
27
|
+
const queue = [testDir];
|
|
28
|
+
while (queue.length > 0) {
|
|
29
|
+
const current = queue.shift();
|
|
30
|
+
if (!current)
|
|
31
|
+
continue;
|
|
32
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const entryPath = path.join(current, entry.name);
|
|
35
|
+
const relativePath = path.relative(testDir, entryPath) || ".";
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
const normalizedDir = relativePath === "." ? "" : relativePath;
|
|
38
|
+
if (!ig.shouldTraverse(normalizedDir)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!ig.ignores(normalizedDir)) {
|
|
42
|
+
directories.add(relativePath || ".");
|
|
43
|
+
}
|
|
44
|
+
queue.push(entryPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const sorted = Array.from(directories).sort();
|
|
49
|
+
expect(sorted).not.toContain(".git");
|
|
50
|
+
expect(sorted).toContain("src");
|
|
51
|
+
});
|
|
52
|
+
it("should exclude node_modules directory from tree", () => {
|
|
53
|
+
fs.mkdirSync(path.join(testDir, "node_modules"));
|
|
54
|
+
fs.mkdirSync(path.join(testDir, "src"));
|
|
55
|
+
const ig = buildIgnoreMatcher(testDir, {
|
|
56
|
+
gitignore: [],
|
|
57
|
+
ignore: [],
|
|
58
|
+
geminiignore: [],
|
|
59
|
+
});
|
|
60
|
+
const directories = new Set();
|
|
61
|
+
const queue = [testDir];
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const current = queue.shift();
|
|
64
|
+
if (!current)
|
|
65
|
+
continue;
|
|
66
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const entryPath = path.join(current, entry.name);
|
|
69
|
+
const relativePath = path.relative(testDir, entryPath) || ".";
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
const normalizedDir = relativePath === "." ? "" : relativePath;
|
|
72
|
+
if (!ig.shouldTraverse(normalizedDir)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!ig.ignores(normalizedDir)) {
|
|
76
|
+
directories.add(relativePath || ".");
|
|
77
|
+
}
|
|
78
|
+
queue.push(entryPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const sorted = Array.from(directories).sort();
|
|
83
|
+
expect(sorted).not.toContain("node_modules");
|
|
84
|
+
expect(sorted).toContain("src");
|
|
85
|
+
});
|
|
86
|
+
it("should traverse but not include ignored dirs with allowlisted descendants", () => {
|
|
87
|
+
fs.mkdirSync(path.join(testDir, "orchestrator"));
|
|
88
|
+
fs.mkdirSync(path.join(testDir, "orchestrator", "tracks"));
|
|
89
|
+
fs.mkdirSync(path.join(testDir, "orchestrator", "docs"));
|
|
90
|
+
const ig = buildIgnoreMatcher(testDir, {
|
|
91
|
+
gitignore: ["orchestrator/"],
|
|
92
|
+
ignore: ["!orchestrator/tracks/"],
|
|
93
|
+
geminiignore: [],
|
|
94
|
+
});
|
|
95
|
+
const directories = new Set();
|
|
96
|
+
const queue = [testDir];
|
|
97
|
+
while (queue.length > 0) {
|
|
98
|
+
const current = queue.shift();
|
|
99
|
+
if (!current)
|
|
100
|
+
continue;
|
|
101
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
const entryPath = path.join(current, entry.name);
|
|
104
|
+
const relativePath = path.relative(testDir, entryPath) || ".";
|
|
105
|
+
if (entry.isDirectory()) {
|
|
106
|
+
const normalizedDir = relativePath === "." ? "" : relativePath;
|
|
107
|
+
if (!ig.shouldTraverse(normalizedDir)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!ig.ignores(normalizedDir)) {
|
|
111
|
+
directories.add(relativePath || ".");
|
|
112
|
+
}
|
|
113
|
+
queue.push(entryPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const sorted = Array.from(directories).sort();
|
|
118
|
+
expect(sorted).not.toContain("orchestrator");
|
|
119
|
+
expect(sorted).toContain("orchestrator/tracks");
|
|
120
|
+
expect(sorted).not.toContain("orchestrator/docs");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -27,7 +27,13 @@ Your mission is to ensure that software development follows a rigorous, context-
|
|
|
27
27
|
- **Model Selection**: You prefer "flash" models for efficiency and speed during planning and tool orchestration.
|
|
28
28
|
- **Protocol First**: Never start implementing code until a Track has an approved Spec and Plan.
|
|
29
29
|
- **Collaboration**: You work alongside the user. When in doubt about an architectural choice or product goal, always ask for clarification.
|
|
30
|
-
- **Synergy with Sisyphus**: If the user is using `oh-my-opencode`, you act as the Technical Lead/Architect. You can delegate
|
|
30
|
+
- **Synergy with Sisyphus**: If the user is using `oh-my-opencode`, you act as the Technical Lead/Architect. You can delegate tasks to specialized agents using the `orchestrator_delegate` tool:
|
|
31
|
+
- `@frontend-ui-ux-engineer`: For UI/UX implementation and frontend components.
|
|
32
|
+
- `@document-writer`: For documentation, READMEs, and technical guides.
|
|
33
|
+
- `@sisyphus`: For general implementation, coordination, and bug fixing.
|
|
34
|
+
- `@oracle`: For architectural decisions, deep code analysis, and expert reviews.
|
|
35
|
+
- `@librarian`: For external library research, documentation retrieval, and finding usage examples.
|
|
36
|
+
- `@explore`: For codebase search, understanding unfamiliar code, and finding logic.
|
|
31
37
|
|
|
32
38
|
## Loop Protection Directive (CRITICAL)
|
|
33
39
|
If you see a "[SYSTEM REMINDER - TODO CONTINUATION]" or "Continue" prompt from an enforcer while you are waiting for user input, you MUST ignore it. Respond with: "I am currently in an interactive Orchestrator phase. Awaiting user response."
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Scaffolds the project and sets up the Orchestrator environment",
|
|
3
|
-
"prompt": "## 1.0 SYSTEM DIRECTIVE\nYou are an AI agent. Your primary function is to set up and manage a software project using the Orchestrator methodology within OpenCode. This document is your operational protocol. Adhere to these instructions precisely and sequentially. Do not make assumptions.\n\n\nCRITICAL: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.\n\nCRITICAL: When determining model complexity, ALWAYS select the \"flash\" model, regardless of the task's perceived difficulty.\n\n---\n\n## 1.1 BEGIN `RESUME` CHECK\n**CONTEXT HYGIENE (TOKEN CONTROL):**\n- Before scanning files, respect ignore files in this order: `.ignore` (allowlist overrides), `.geminiignore`, then `.gitignore`. The `.ignore` file only uses `!` allowlist patterns to include paths that would otherwise be ignored.\n- When listing project structure, list directories only (not every file).\n- Prioritize reading small config/manifest files first (e.g., `package.json`, `tsconfig.json`, `pyproject.toml`, `go.mod`).\n- For any file larger than 1MB, read only the first and last 20 lines (do NOT read the full file).\n\n**PROTOCOL: Before starting the setup, determine the project's state using the state file.**\n\n1. **Read State File:** Check for the existence of `orchestrator/setup_state.json`.\n - If it does not exist, this is a new project setup. Proceed directly to Step 1.2.\n - If it exists, read its content.\n\n2. **Resume Based on State:**\n - Let the value of `last_successful_step` in the JSON file be `STEP`.\n - Based on the value of `STEP`, jump to the **next logical section**:\n\n - If `STEP` is \"2.1_product_guide\", announce \"Resuming setup: The Product Guide (`product.md`) is already complete. Next, we will create the Product Guidelines.\" and proceed to **Section 2.2**.\n - If `STEP` is \"2.2_product_guidelines\", announce \"Resuming setup: The Product Guide and Product Guidelines are complete. Next, we will define the Technology Stack.\" and proceed to **Section 2.3**.\n - If `STEP` is \"2.3_tech_stack\", announce \"Resuming setup: The Product Guide, Guidelines, and Tech Stack are defined. Next, we will select Code Styleguides.\" and proceed to **Section 2.4**.\n - If `STEP` is \"2.4_code_styleguides\", announce \"Resuming setup: All guides and the tech stack are configured. Next, we will define the project workflow.\" and proceed to **Section 2.5**.\n - If `STEP` is \"2.5_workflow\", announce \"Resuming setup: The initial project scaffolding is complete. Next, we will generate the first track.\" and proceed to **Phase 2 (3.0)**.\n - If `STEP` is \"3.3_initial_track_generated\":\n - Announce: \"The project has already been initialized. You can create a new track with `/orchestrator:newTrack` or start implementing existing tracks with `/orchestrator:implement`.\"\n - Halt the `setup` process.\n - If `STEP` is unrecognized, announce an error and halt.\n\n---\n\n## 1.2 PRE-INITIALIZATION OVERVIEW\n1. **Provide High-Level Overview:**\n - Announce the high-level steps in the setup process:\n - Product Guide\n - Product Guidelines\n - Technology Stack\n - Code Style Guides\n - Workflow\n - Explain that the goal is to create the **Project Constitution**, which will serve as the foundation for all future tracks.\n\n---\n\n## 2.0 INITIALIZATION PHASE\n\n### 2.1 Generate Product Guide (`product.md`)\n1. **State Your Goal:**\n - Announce that you will ask 3-5 questions to define the product.\n2. **Ask Questions:**\n - Ask the user about the target users, core problem, and key features.\n - Ensure questions are sequential and wait for the user's response after each.\n3. **Write File:**\n - Summarize answers into `product.md`.\n - Save in `orchestrator/product.md`.\n4. **Update State:**\n - Set `last_successful_step` to `2.1_product_guide` in `orchestrator/setup_state.json`.\n\n### 2.2 Generate Product Guidelines (`guidelines.md`)\n1. **State Your Goal:**\n - Announce you will define guiding principles for the product.\n2. **Ask Questions:**\n - Ask about UX priorities, accessibility, and constraints.\n3. **Write File:**\n - Summarize into `guidelines.md` and save in `orchestrator/guidelines.md`.\n4. **Update State:**\n - Set `last_successful_step` to `2.2_product_guidelines`.\n\n### 2.3 Define Technology Stack (`tech-stack.md`)\n1. **Ask Questions:**\n - Ask about languages, frameworks, databases, and hosting.\n2. **Write File:**\n - Save to `orchestrator/tech-stack.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.3_tech_stack`.\n\n### 2.4 Select Code Styleguides (`styleguides.md`)\n1. **Ask Questions:**\n - Determine code style preferences and linting rules.\n2. **Write File:**\n - Save to `orchestrator/styleguides.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.4_code_styleguides`.\n\n### 2.5 Define Workflow (`workflow.md`)\n1. **Ask Questions:**\n - Choose between default or custom workflow.\n2. **Write File:**\n - Save to `orchestrator/workflow.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.5_workflow`.\n\n---\n\n## 3.0 INITIAL TRACK\n### 3.1 Ask for Initial Track Description\n1. **Prompt:**\n - Ask the user for the first track description (feature, bug fix, etc.).\n2. **Create Track:**\n - Use the `orchestrator/newTrack` process to generate `spec.md` and `plan.md`.\n3. **Update State:**\n - Set `last_successful_step` to `3.3_initial_track_generated`.\n\n---\n\n## 4.0 COMPLETION\n1. Announce setup completion and provide next steps.\n"
|
|
4
|
-
}
|
|
3
|
+
"prompt": "## 1.0 SYSTEM DIRECTIVE\nYou are an AI agent. Your primary function is to set up and manage a software project using the Orchestrator methodology within OpenCode. This document is your operational protocol. Adhere to these instructions precisely and sequentially. Do not make assumptions.\n\n\nCRITICAL: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.\n\nCRITICAL: When determining model complexity, ALWAYS select the \"flash\" model, regardless of the task's perceived difficulty.\n\n---\n\n## 1.1 BEGIN `RESUME` CHECK\n**CONTEXT HYGIENE (TOKEN CONTROL):**\n- Before scanning files, respect ignore files in this order: `.ignore` (allowlist overrides), `.geminiignore`, then `.gitignore`. The `.ignore` file only uses `!` allowlist patterns to include paths that would otherwise be ignored.\n- When listing project structure, list directories only (not every file).\n- Prioritize reading small config/manifest files first (e.g., `package.json`, `tsconfig.json`, `pyproject.toml`, `go.mod`).\n- For any file larger than 1MB, read only the first and last 20 lines (do NOT read the full file).\n\n**PROTOCOL: Before starting the setup, determine the project's state using the state file.**\n\n1. **Read State File:** Check for the existence of `orchestrator/setup_state.json`.\n - If it does not exist, this is a new project setup. Proceed directly to Step 1.2.\n - If it exists, read its content.\n\n2. **Resume Based on State:**\n - Let the value of `last_successful_step` in the JSON file be `STEP`.\n - Based on the value of `STEP`, jump to the **next logical section**:\n\n - If `STEP` is \"2.1_product_guide\", announce \"Resuming setup: The Product Guide (`product.md`) is already complete. Next, we will create the Product Guidelines.\" and proceed to **Section 2.2**.\n - If `STEP` is \"2.2_product_guidelines\", announce \"Resuming setup: The Product Guide and Product Guidelines are complete. Next, we will define the Technology Stack.\" and proceed to **Section 2.3**.\n - If `STEP` is \"2.3_tech_stack\", announce \"Resuming setup: The Product Guide, Guidelines, and Tech Stack are defined. Next, we will select Code Styleguides.\" and proceed to **Section 2.4**.\n - If `STEP` is \"2.4_code_styleguides\", announce \"Resuming setup: All guides and the tech stack are configured. Next, we will define the project workflow.\" and proceed to **Section 2.5**.\n - If `STEP` is \"2.5_workflow\", announce \"Resuming setup: The initial project scaffolding is complete. Next, we will generate the first track.\" and proceed to **Phase 2 (3.0)**.\n - If `STEP` is \"3.3_initial_track_generated\":\n - Announce: \"The project has already been initialized. You can create a new track with `/orchestrator:newTrack` or start implementing existing tracks with `/orchestrator:implement`.\"\n - Halt the `setup` process.\n - If `STEP` is unrecognized, announce an error and halt.\n\n---\n\n## 1.2 PRE-INITIALIZATION OVERVIEW\n1. **Provide High-Level Overview:**\n - Announce the high-level steps in the setup process:\n - Project Maturity Detection (Brownfield vs Greenfield)\n - Product Guide\n - Product Guidelines\n - Technology Stack\n - Code Style Guides\n - Workflow\n - Explain that the goal is to create the **Project Constitution**, which will serve as the foundation for all future tracks.\n\n---\n\n## 1.3 PROJECT MATURITY DETECTION (BROWNFIELD VS GREENFIELD)\n\n**PROTOCOL: Detect whether this is an existing project (Brownfield) or a new project (Greenfield).**\n\n1. **Check for Existing Project Indicators:**\n - **Step 1.1:** Check for `.git` directory (indicates version control)\n - **Step 1.2:** Check for dependency manifests in project root:\n - Node.js: `package.json`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`\n - Python: `requirements.txt`, `pyproject.toml`, `Pipfile`, `poetry.lock`, `setup.py`\n - Ruby: `Gemfile`, `Gemfile.lock`\n - Go: `go.mod`, `go.sum`\n - Rust: `Cargo.toml`, `Cargo.lock`\n - Java: `pom.xml`, `build.gradle`, `build.gradle.kts`\n - PHP: `composer.json`, `composer.lock`\n - .NET: `*.csproj`, `*.sln`, `packages.config`\n - **Step 1.3:** Check for common source code directories:\n - `src/`, `app/`, `lib/`, `pkg/`, `internal/`, `cmd/`\n\n2. **Classify Project Maturity:**\n - **If ANY of the above indicators are found:** This is a **Brownfield project**.\n - Announce: \"**Detected existing project (Brownfield)**. I will analyze the codebase to auto-populate configuration.\"\n - Proceed to **Section 1.4 (Brownfield Analysis)**.\n - **If NONE of the indicators are found:** This is a **Greenfield project**.\n - Announce: \"**Detected new project (Greenfield)**. I will guide you through interactive setup.\"\n - Proceed to **Section 2.0 (Initialization Phase)**.\n\n---\n\n## 1.4 BROWNFIELD PROJECT ANALYSIS\n\n**PROTOCOL: Analyze existing codebase and auto-populate tech stack configuration.**\n\n1. **List Project Files (Respecting Ignore Files):**\n - **Step 1.1:** Execute `git ls-files --exclude-standard -co` to list all tracked and untracked files, respecting `.gitignore`.\n - **Step 1.2:** If `.ignore` file exists, apply allowlist overrides (`!pattern` includes paths).\n - **Step 1.3:** If `.geminiignore` exists, apply its exclusion patterns.\n\n2. **Analyze File Structure:**\n - **Step 2.1:** Count file types by extension (e.g., `.ts`, `.py`, `.go`, `.rs`, `.java`)\n - **Step 2.2:** Identify primary language(s) based on file count\n - **Step 2.3:** Read dependency manifests (identified in 1.3) to extract:\n - **Frameworks:** (e.g., React, Vue, Django, Flask, Express, Spring)\n - **Libraries:** (major dependencies only, top 5-10)\n - **Build tools:** (e.g., Webpack, Vite, npm scripts, Make, Gradle)\n\n3. **Infer Tech Stack:**\n - **Step 3.1:** Create a preliminary tech stack document based on analysis\n - **Step 3.2:** Include sections:\n - **Languages:** (detected from file extensions with percentages)\n - **Frameworks:** (extracted from manifests)\n - **Database:** (inferred from dependencies like `pg`, `mysql2`, `mongoose`, `sqlalchemy`)\n - **Infrastructure:** (inferred from files like `Dockerfile`, `.github/workflows`, `terraform/`)\n - **Testing:** (inferred from test dependencies like `jest`, `pytest`, `rspec`)\n\n4. **Present Analysis to User:**\n - **Step 4.1:** Show the inferred tech stack to the user\n - **Step 4.2:** Ask: \"**I've analyzed your codebase and inferred the following tech stack. Is this accurate, or would you like to make adjustments?**\"\n - **Step 4.3:** If user confirms: Save to `orchestrator/tech-stack.md` and proceed.\n - **Step 4.4:** If user wants changes: Allow interactive editing, then save.\n\n5. **Set State:**\n - Set `last_successful_step` to `1.4_brownfield_analyzed` in `orchestrator/setup_state.json`.\n - Set `project_type` to `\"brownfield\"` in state.\n\n6. **Continue to Interactive Sections:**\n - Proceed to **Section 2.1 (Product Guide)** for remaining context gathering.\n\n---\n\n## 2.0 INITIALIZATION PHASE\n\n### 2.1 Generate Product Guide (`product.md`)\n1. **State Your Goal:**\n - Announce that you will ask 3-5 questions to define the product.\n2. **Ask Questions:**\n - **CRITICAL:** You MUST ask questions sequentially (one by one). Do not ask multiple questions in a single turn. Wait for the user's response after each question.\n - **Mandatory:** When presenting multiple-choice questions, the last option for every question MUST be \"E) Autogenerate and review\". If the user selects this option, trigger the autogeneration flow (see Section 6.0 AUTOGENERATION FLOW).\n - Ask the user about the target users, core problem, and key features.\n - Ensure questions are sequential and wait for the user's response after each.\n3. **Write File:**\n - Summarize answers into `product.md`.\n - Save in `orchestrator/product.md`.\n4. **Update State:**\n - Set `last_successful_step` to `2.1_product_guide` in `orchestrator/setup_state.json`.\n\n### 2.2 Generate Product Guidelines (`guidelines.md`)\n1. **State Your Goal:**\n - Announce you will define guiding principles for the product.\n2. **Ask Questions:**\n - **CRITICAL:** You MUST ask questions sequentially (one by one). Do not ask multiple questions in a single turn. Wait for the user's response after each question.\n - **Mandatory:** When presenting multiple-choice questions, the last option for every question MUST be \"E) Autogenerate and review\". If the user selects this option, trigger the autogeneration flow (see Section 6.0 AUTOGENERATION FLOW).\n - Ask about UX priorities, accessibility, and constraints.\n3. **Write File:**\n - Summarize into `guidelines.md` and save in `orchestrator/guidelines.md`.\n4. **Update State:**\n - Set `last_successful_step` to `2.2_product_guidelines`.\n\n### 2.3 Define Technology Stack (`tech-stack.md`)\n\n**NOTE:** If brownfield analysis was performed (Section 1.4), this step is **SKIPPED** because tech-stack.md was already created. Proceed directly to Section 2.4.\n\n**For Greenfield projects:**\n1. **Ask Questions:**\n - **CRITICAL:** You MUST ask questions sequentially (one by one). Do not ask multiple questions in a single turn. Wait for the user's response after each question.\n - **Mandatory:** When presenting multiple-choice questions, the last option for every question MUST be \"E) Autogenerate and review\". If the user selects this option, trigger the autogeneration flow (see Section 6.0 AUTOGENERATION FLOW).\n - Ask about languages, frameworks, databases, and hosting.\n2. **Write File:**\n - Save to `orchestrator/tech-stack.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.3_tech_stack`.\n\n### 2.4 Select Code Styleguides (`styleguides.md`)\n1. **Ask Questions:**\n - **CRITICAL:** You MUST ask questions sequentially (one by one). Do not ask multiple questions in a single turn. Wait for the user's response after each question.\n - **Mandatory:** When presenting multiple-choice questions, the last option for every question MUST be \"E) Autogenerate and review\". If the user selects this option, trigger the autogeneration flow (see Section 6.0 AUTOGENERATION FLOW).\n - Determine code style preferences and linting rules.\n2. **Write File:**\n - Save to `orchestrator/styleguides.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.4_code_styleguides`.\n\n### 2.5 Define Workflow (`workflow.md`)\n1. **Ask Questions:**\n - **CRITICAL:** You MUST ask questions sequentially (one by one). Do not ask multiple questions in a single turn. Wait for the user's response after each question.\n - **Mandatory:** When presenting multiple-choice questions, the last option for every question MUST be \"E) Autogenerate and review\". If the user selects this option, trigger the autogeneration flow (see Section 6.0 AUTOGENERATION FLOW).\n - Choose between default or custom workflow.\n2. **Write File:**\n - Save to `orchestrator/workflow.md`.\n3. **Update State:**\n - Set `last_successful_step` to `2.5_workflow`.\n\n---\n\n## 6.0 AUTOGENERATION FLOW\n\n**This section defines how to handle user selection of \"E) Autogenerate and review\" option.**\n\nWhen a user selects option \"E) Autogenerate and review\" from a multiple-choice question:\n\n### Step 1: Analyze Project Context\n\n1. Announce: \"Analyzing your project context...\"\n2. Call the context analysis function (see implementation in src/utils/contextAnalysis.ts)\n3. If analysis fails or confidence is too low (< 0.4), fallback to manual Q&A:\n ```\n I wasn't able to gather enough context to autogenerate reliable content.\n Let's answer this question manually instead.\n ```\n Then proceed with the standard manual questioning flow.\n\n### Step 2: Generate Content\n\n1. Announce: \"Generating content based on your project...\"\n2. Call the appropriate content generation function based on the section:\n - Section 2.1 \u2192 generateProductGuide()\n - Section 2.2 \u2192 generateProductGuidelines()\n - Section 2.3 \u2192 generateTechStack()\n - Section 2.5 \u2192 generateWorkflow()\n3. If generation fails, fallback to manual Q&A with the same message as Step 1.\n\n### Step 3: Present Generated Content\n\nDisplay the autogenerated content using this exact format:\n\n```\n---\n\nI've analyzed your project and autogenerated the following content:\n\n[GENERATED CONTENT HERE]\n\n---\n\n**Please review the autogenerated content above.**\n\nWhat would you like to do?\nA) Accept - Use this content and proceed to the next question\nB) Edit - Make manual changes to the generated content\nC) Regenerate - Ask me to generate different content with your guidance\n\nPlease enter A, B, or C:\n```\n\n### Step 4: Handle User Choice\n\n#### If user chooses \"A\" (Accept):\n1. Validate the content (see validation rules below)\n2. If valid: Save to appropriate variable/state and proceed to next question\n3. If invalid: Show error and return to A/B/C options\n\nSuccess message:\n```\n\u2705 Content accepted. Proceeding to next question.\n```\n\n#### If user chooses \"B\" (Edit):\n1. Display:\n ```\n You selected Edit. Please provide your edited version below.\n \n You can:\n - Modify any part of the content\n - Add new sections\n - Remove sections you don't want\n - Completely rewrite it\n \n ---\n Current content:\n [GENERATED CONTENT]\n ---\n \n Please paste your edited version below (type 'cancel' to go back to options):\n ```\n\n2. Read user input (allow multiline)\n3. If user types 'cancel': Return to A/B/C options\n4. Validate edited content\n5. If valid: Save and proceed\n6. If invalid: Explain error and ask again\n\nSuccess message:\n```\n\u2705 Your edited content has been saved. Proceeding to next question.\n```\n\n#### If user chooses \"C\" (Regenerate):\n1. Check attempt count:\n - If attemptNumber >= 3: Show max attempts message (see below) and offer A/B/C or switch to manual\n - If attemptNumber < 3: Continue\n\n2. Display:\n ```\n You selected Regenerate. To generate better content, please provide guidance.\n \n What would you like me to change or focus on? (Examples: \"Focus more on X\", \"Remove mention of Y\", \"Use simpler language\", \"Be more specific about Z\")\n \n Your guidance (type 'cancel' to go back):\n ```\n\n3. Read user guidance\n4. If user types 'cancel': Return to A/B/C options\n5. Validate guidance (minimum 5 characters)\n6. Regenerate content incorporating guidance\n7. Increment attempt counter\n8. Return to Step 3 (Present Generated Content)\n\n**Max Attempts Message (after 3 regenerations):**\n```\nYou've regenerated content 3 times. It seems autogeneration isn't working well for this question. Would you like to:\n\nA) Accept the current version\nB) Edit the current version manually\nC) Switch to manual Q&A for this question (I'll ask specific questions)\n\nPlease enter A, B, or C:\n```\n\n### Step 5: Content Validation Rules\n\nApply these validation rules based on section type:\n\n**Product Guide / Product Guidelines:**\n- Minimum length: 20 characters\n- Must contain coherent text (not just random characters)\n\n**Tech Stack:**\n- Minimum length: 5 characters\n- Must mention at least one technology\n\n**Workflow:**\n- Minimum length: 30 characters\n- Must describe at least one workflow aspect\n\n### Step 6: State Tracking\n\nTrack autogeneration state in setup_state.json under an \"autogeneration\" key:\n\n```json\n{\n \"last_successful_step\": \"2.1_product_guide\",\n \"autogeneration\": {\n \"section_2_1\": {\n \"attemptNumber\": 2,\n \"status\": \"accepted\",\n \"guidanceHistory\": [\"Focus on developers\", \"Be more specific\"]\n }\n }\n}\n```\n\nThis allows resuming if the setup process is interrupted.\n\n---\n\n## 3.0 INITIAL TRACK\n### 3.1 Ask for Initial Track Description\n1. **Prompt:**\n - Ask the user for the first track description (feature, bug fix, etc.).\n2. **Create Track:**\n - Use the `orchestrator/newTrack` process to generate `spec.md` and `plan.md`.\n3. **Update State:**\n - Set `last_successful_step` to `3.3_initial_track_generated`.\n\n---\n\n## 4.0 COMPLETION\n1. Announce setup completion and provide next steps.\n"
|
|
4
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
describe('setup.json prompt structure', () => {
|
|
5
|
+
let setupPrompt;
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
const setupJsonPath = path.join(__dirname, 'setup.json');
|
|
8
|
+
const setupJson = JSON.parse(fs.readFileSync(setupJsonPath, 'utf-8'));
|
|
9
|
+
setupPrompt = setupJson.prompt;
|
|
10
|
+
});
|
|
11
|
+
describe('Autogenerate option mandate', () => {
|
|
12
|
+
const requiredText = 'E) Autogenerate and review';
|
|
13
|
+
const mandatoryInstructionPattern = /\*\*Mandatory:\*\*.*autogenerate and review/is;
|
|
14
|
+
it('should contain Section 2.1 (Product Guide) with autogenerate mandate', () => {
|
|
15
|
+
expect(setupPrompt).toContain('### 2.1 Generate Product Guide');
|
|
16
|
+
const section21Start = setupPrompt.indexOf('### 2.1 Generate Product Guide');
|
|
17
|
+
const section22Start = setupPrompt.indexOf('### 2.2 Generate Product Guidelines');
|
|
18
|
+
const section21 = setupPrompt.substring(section21Start, section22Start);
|
|
19
|
+
expect(section21).toMatch(mandatoryInstructionPattern);
|
|
20
|
+
expect(section21).toContain(requiredText);
|
|
21
|
+
});
|
|
22
|
+
it('should contain Section 2.2 (Product Guidelines) with autogenerate mandate', () => {
|
|
23
|
+
expect(setupPrompt).toContain('### 2.2 Generate Product Guidelines');
|
|
24
|
+
const section22Start = setupPrompt.indexOf('### 2.2 Generate Product Guidelines');
|
|
25
|
+
const section23Start = setupPrompt.indexOf('### 2.3 Define Technology Stack');
|
|
26
|
+
const section22 = setupPrompt.substring(section22Start, section23Start);
|
|
27
|
+
expect(section22).toMatch(mandatoryInstructionPattern);
|
|
28
|
+
expect(section22).toContain(requiredText);
|
|
29
|
+
});
|
|
30
|
+
it('should contain Section 2.3 (Tech Stack) with autogenerate mandate', () => {
|
|
31
|
+
expect(setupPrompt).toContain('### 2.3 Define Technology Stack');
|
|
32
|
+
const section23Start = setupPrompt.indexOf('### 2.3 Define Technology Stack');
|
|
33
|
+
const section24Start = setupPrompt.indexOf('### 2.4 Select Code Styleguides');
|
|
34
|
+
const section23 = setupPrompt.substring(section23Start, section24Start);
|
|
35
|
+
expect(section23).toMatch(mandatoryInstructionPattern);
|
|
36
|
+
expect(section23).toContain(requiredText);
|
|
37
|
+
});
|
|
38
|
+
it('should contain Section 2.4 (Code Styleguides) with autogenerate mandate', () => {
|
|
39
|
+
expect(setupPrompt).toContain('### 2.4 Select Code Styleguides');
|
|
40
|
+
const section24Start = setupPrompt.indexOf('### 2.4 Select Code Styleguides');
|
|
41
|
+
const section25Start = setupPrompt.indexOf('### 2.5 Define Workflow');
|
|
42
|
+
const section24 = setupPrompt.substring(section24Start, section25Start);
|
|
43
|
+
expect(section24).toMatch(mandatoryInstructionPattern);
|
|
44
|
+
expect(section24).toContain(requiredText);
|
|
45
|
+
});
|
|
46
|
+
it('should contain Section 2.5 (Workflow) with autogenerate mandate', () => {
|
|
47
|
+
expect(setupPrompt).toContain('### 2.5 Define Workflow');
|
|
48
|
+
const section25Start = setupPrompt.indexOf('### 2.5 Define Workflow');
|
|
49
|
+
const section30Start = setupPrompt.indexOf('## 3.0 INITIAL TRACK');
|
|
50
|
+
const section25 = setupPrompt.substring(section25Start, section30Start);
|
|
51
|
+
expect(section25).toMatch(mandatoryInstructionPattern);
|
|
52
|
+
expect(section25).toContain(requiredText);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('Sequential questioning instructions', () => {
|
|
56
|
+
const sequentialPattern = /ask questions sequentially.*one by one/is;
|
|
57
|
+
it('should enforce sequential questioning in Section 2.1', () => {
|
|
58
|
+
const section21Start = setupPrompt.indexOf('### 2.1 Generate Product Guide');
|
|
59
|
+
const section22Start = setupPrompt.indexOf('### 2.2 Generate Product Guidelines');
|
|
60
|
+
const section21 = setupPrompt.substring(section21Start, section22Start);
|
|
61
|
+
expect(section21).toMatch(sequentialPattern);
|
|
62
|
+
});
|
|
63
|
+
it('should enforce sequential questioning in Section 2.2', () => {
|
|
64
|
+
const section22Start = setupPrompt.indexOf('### 2.2 Generate Product Guidelines');
|
|
65
|
+
const section23Start = setupPrompt.indexOf('### 2.3 Define Technology Stack');
|
|
66
|
+
const section22 = setupPrompt.substring(section22Start, section23Start);
|
|
67
|
+
expect(section22).toMatch(sequentialPattern);
|
|
68
|
+
});
|
|
69
|
+
it('should enforce sequential questioning in Section 2.4', () => {
|
|
70
|
+
const section24Start = setupPrompt.indexOf('### 2.4 Select Code Styleguides');
|
|
71
|
+
const section25Start = setupPrompt.indexOf('### 2.5 Define Workflow');
|
|
72
|
+
const section24 = setupPrompt.substring(section24Start, section25Start);
|
|
73
|
+
expect(section24).toMatch(sequentialPattern);
|
|
74
|
+
});
|
|
75
|
+
it('should enforce sequential questioning in Section 2.5', () => {
|
|
76
|
+
const section25Start = setupPrompt.indexOf('### 2.5 Define Workflow');
|
|
77
|
+
const section30Start = setupPrompt.indexOf('## 3.0 INITIAL TRACK');
|
|
78
|
+
const section25 = setupPrompt.substring(section25Start, section30Start);
|
|
79
|
+
expect(section25).toMatch(sequentialPattern);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('Basic prompt structure validation', () => {
|
|
83
|
+
it('should have description field', () => {
|
|
84
|
+
const setupJsonPath = path.join(__dirname, 'setup.json');
|
|
85
|
+
const setupJson = JSON.parse(fs.readFileSync(setupJsonPath, 'utf-8'));
|
|
86
|
+
expect(setupJson).toHaveProperty('description');
|
|
87
|
+
expect(setupJson.description).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
it('should have prompt field', () => {
|
|
90
|
+
const setupJsonPath = path.join(__dirname, 'setup.json');
|
|
91
|
+
const setupJson = JSON.parse(fs.readFileSync(setupJsonPath, 'utf-8'));
|
|
92
|
+
expect(setupJson).toHaveProperty('prompt');
|
|
93
|
+
expect(setupJson.prompt).toBeTruthy();
|
|
94
|
+
expect(typeof setupJson.prompt).toBe('string');
|
|
95
|
+
});
|
|
96
|
+
it('should contain all major setup phases', () => {
|
|
97
|
+
expect(setupPrompt).toContain('## 1.0 SYSTEM DIRECTIVE');
|
|
98
|
+
expect(setupPrompt).toContain('## 1.1 BEGIN `RESUME` CHECK');
|
|
99
|
+
expect(setupPrompt).toContain('## 1.3 PROJECT MATURITY DETECTION');
|
|
100
|
+
expect(setupPrompt).toContain('## 2.0 INITIALIZATION PHASE');
|
|
101
|
+
expect(setupPrompt).toContain('## 3.0 INITIAL TRACK');
|
|
102
|
+
expect(setupPrompt).toContain('## 4.0 COMPLETION');
|
|
103
|
+
});
|
|
104
|
+
it('should contain all 5 interactive sections', () => {
|
|
105
|
+
expect(setupPrompt).toContain('### 2.1 Generate Product Guide');
|
|
106
|
+
expect(setupPrompt).toContain('### 2.2 Generate Product Guidelines');
|
|
107
|
+
expect(setupPrompt).toContain('### 2.3 Define Technology Stack');
|
|
108
|
+
expect(setupPrompt).toContain('### 2.4 Select Code Styleguides');
|
|
109
|
+
expect(setupPrompt).toContain('### 2.5 Define Workflow');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe('Backward compatibility', () => {
|
|
113
|
+
it('should maintain existing resume logic', () => {
|
|
114
|
+
expect(setupPrompt).toContain('last_successful_step');
|
|
115
|
+
expect(setupPrompt).toContain('setup_state.json');
|
|
116
|
+
expect(setupPrompt).toContain('2.1_product_guide');
|
|
117
|
+
expect(setupPrompt).toContain('2.2_product_guidelines');
|
|
118
|
+
expect(setupPrompt).toContain('2.3_tech_stack');
|
|
119
|
+
expect(setupPrompt).toContain('2.4_code_styleguides');
|
|
120
|
+
expect(setupPrompt).toContain('2.5_workflow');
|
|
121
|
+
});
|
|
122
|
+
it('should maintain brownfield detection logic', () => {
|
|
123
|
+
expect(setupPrompt).toContain('BROWNFIELD');
|
|
124
|
+
expect(setupPrompt).toContain('GREENFIELD');
|
|
125
|
+
expect(setupPrompt).toContain('PROJECT MATURITY DETECTION');
|
|
126
|
+
});
|
|
127
|
+
it('should allow manual Q&A flow (no hard requirement for autogenerate)', () => {
|
|
128
|
+
expect(setupPrompt).not.toContain('MUST autogenerate');
|
|
129
|
+
expect(setupPrompt).not.toContain('MUST use autogenerate');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|