gswd 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/gswd-tools.cjs +228 -0
- package/commands/gswd/imagine.md +7 -1
- package/commands/gswd/start.md +507 -32
- package/dist/lib/audit.d.ts +205 -0
- package/dist/lib/audit.js +805 -0
- package/dist/lib/bootstrap.d.ts +103 -0
- package/dist/lib/bootstrap.js +563 -0
- package/dist/lib/compile.d.ts +239 -0
- package/dist/lib/compile.js +1152 -0
- package/dist/lib/config.d.ts +49 -0
- package/dist/lib/config.js +150 -0
- package/dist/lib/imagine-agents.d.ts +54 -0
- package/dist/lib/imagine-agents.js +185 -0
- package/dist/lib/imagine-gate.d.ts +47 -0
- package/dist/lib/imagine-gate.js +131 -0
- package/dist/lib/imagine-input.d.ts +46 -0
- package/dist/lib/imagine-input.js +233 -0
- package/dist/lib/imagine-synthesis.d.ts +90 -0
- package/dist/lib/imagine-synthesis.js +453 -0
- package/dist/lib/imagine.d.ts +56 -0
- package/dist/lib/imagine.js +413 -0
- package/dist/lib/intake.d.ts +27 -0
- package/dist/lib/intake.js +82 -0
- package/dist/lib/parse.d.ts +59 -0
- package/dist/lib/parse.js +171 -0
- package/dist/lib/render.d.ts +309 -0
- package/dist/lib/render.js +624 -0
- package/dist/lib/specify-agents.d.ts +120 -0
- package/dist/lib/specify-agents.js +269 -0
- package/dist/lib/specify-journeys.d.ts +124 -0
- package/dist/lib/specify-journeys.js +279 -0
- package/dist/lib/specify-nfr.d.ts +45 -0
- package/dist/lib/specify-nfr.js +159 -0
- package/dist/lib/specify-roles.d.ts +46 -0
- package/dist/lib/specify-roles.js +88 -0
- package/dist/lib/specify.d.ts +70 -0
- package/dist/lib/specify.js +676 -0
- package/dist/lib/state.d.ts +140 -0
- package/dist/lib/state.js +340 -0
- package/dist/tests/audit.test.d.ts +4 -0
- package/dist/tests/audit.test.js +1579 -0
- package/dist/tests/bootstrap.test.d.ts +5 -0
- package/dist/tests/bootstrap.test.js +611 -0
- package/dist/tests/compile.test.d.ts +4 -0
- package/dist/tests/compile.test.js +862 -0
- package/dist/tests/config.test.d.ts +4 -0
- package/dist/tests/config.test.js +191 -0
- package/dist/tests/imagine-agents.test.d.ts +6 -0
- package/dist/tests/imagine-agents.test.js +179 -0
- package/dist/tests/imagine-gate.test.d.ts +6 -0
- package/dist/tests/imagine-gate.test.js +264 -0
- package/dist/tests/imagine-input.test.d.ts +6 -0
- package/dist/tests/imagine-input.test.js +283 -0
- package/dist/tests/imagine-synthesis.test.d.ts +7 -0
- package/dist/tests/imagine-synthesis.test.js +380 -0
- package/dist/tests/imagine.test.d.ts +8 -0
- package/dist/tests/imagine.test.js +406 -0
- package/dist/tests/parse.test.d.ts +4 -0
- package/dist/tests/parse.test.js +285 -0
- package/dist/tests/render.test.d.ts +4 -0
- package/dist/tests/render.test.js +236 -0
- package/dist/tests/specify-agents.test.d.ts +4 -0
- package/dist/tests/specify-agents.test.js +352 -0
- package/dist/tests/specify-journeys.test.d.ts +5 -0
- package/dist/tests/specify-journeys.test.js +440 -0
- package/dist/tests/specify-nfr.test.d.ts +4 -0
- package/dist/tests/specify-nfr.test.js +205 -0
- package/dist/tests/specify-roles.test.d.ts +4 -0
- package/dist/tests/specify-roles.test.js +136 -0
- package/dist/tests/specify.test.d.ts +9 -0
- package/dist/tests/specify.test.js +544 -0
- package/dist/tests/state.test.d.ts +4 -0
- package/dist/tests/state.test.js +316 -0
- package/lib/bootstrap.ts +37 -11
- package/lib/compile.ts +426 -4
- package/lib/imagine-agents.ts +53 -7
- package/lib/imagine-synthesis.ts +170 -6
- package/lib/imagine.ts +59 -5
- package/lib/intake.ts +60 -0
- package/lib/parse.ts +2 -1
- package/lib/render.ts +566 -5
- package/lib/specify-agents.ts +25 -3
- package/lib/state.ts +115 -0
- package/package.json +3 -2
- package/templates/gswd/DECISIONS.template.md +3 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSWD Config Module — config.json merge under gswd key
|
|
3
|
+
*
|
|
4
|
+
* Config is stored in .planning/config.json under the `gswd` key.
|
|
5
|
+
* Merge operations NEVER touch keys outside `gswd`.
|
|
6
|
+
*
|
|
7
|
+
* Schema: GSWD_SPEC.md Section 5.2
|
|
8
|
+
*/
|
|
9
|
+
export interface GswdAutoConfig {
|
|
10
|
+
policy: 'strict' | 'balanced' | 'aggressive';
|
|
11
|
+
allow_paid_integrations_under_budget: boolean;
|
|
12
|
+
default_auth_model: string;
|
|
13
|
+
default_data_store: string;
|
|
14
|
+
default_stack: string;
|
|
15
|
+
preapproved_integrations: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface GswdConfig {
|
|
18
|
+
mode: string;
|
|
19
|
+
strict_gates: boolean;
|
|
20
|
+
max_parallel_agents: number;
|
|
21
|
+
external_research: boolean;
|
|
22
|
+
integration_budget_usd_month: number;
|
|
23
|
+
doc_verbosity: string;
|
|
24
|
+
phase_style: string;
|
|
25
|
+
auto: GswdAutoConfig;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Default GSWD config matching GSWD_SPEC Section 5.2.
|
|
30
|
+
*/
|
|
31
|
+
export declare const GSWD_CONFIG_DEFAULTS: GswdConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Read config.json. Returns empty object if missing or invalid.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readConfig(configPath: string): Record<string, unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* Merge GSWD config into config.json under the `gswd` key.
|
|
38
|
+
* - Reads existing config (preserves ALL non-gswd keys)
|
|
39
|
+
* - Deep merges: existing gswd values take precedence over defaults
|
|
40
|
+
* - Fills in missing defaults
|
|
41
|
+
* - Writes back atomically
|
|
42
|
+
*
|
|
43
|
+
* CRITICAL: Never touches any key outside `gswd`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeGswdConfig(configPath: string, gswdOverrides?: Partial<GswdConfig>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get GSWD config with all defaults filled in for missing fields.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getGswdConfig(configPath: string): GswdConfig;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GSWD Config Module — config.json merge under gswd key
|
|
4
|
+
*
|
|
5
|
+
* Config is stored in .planning/config.json under the `gswd` key.
|
|
6
|
+
* Merge operations NEVER touch keys outside `gswd`.
|
|
7
|
+
*
|
|
8
|
+
* Schema: GSWD_SPEC.md Section 5.2
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.GSWD_CONFIG_DEFAULTS = void 0;
|
|
45
|
+
exports.readConfig = readConfig;
|
|
46
|
+
exports.mergeGswdConfig = mergeGswdConfig;
|
|
47
|
+
exports.getGswdConfig = getGswdConfig;
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
49
|
+
const state_js_1 = require("./state.js");
|
|
50
|
+
// ─── Defaults ────────────────────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* Default GSWD config matching GSWD_SPEC Section 5.2.
|
|
53
|
+
*/
|
|
54
|
+
exports.GSWD_CONFIG_DEFAULTS = {
|
|
55
|
+
mode: 'balanced',
|
|
56
|
+
strict_gates: true,
|
|
57
|
+
max_parallel_agents: 4,
|
|
58
|
+
external_research: true,
|
|
59
|
+
integration_budget_usd_month: 100,
|
|
60
|
+
doc_verbosity: 'normal',
|
|
61
|
+
phase_style: 'thin',
|
|
62
|
+
auto: {
|
|
63
|
+
policy: 'balanced',
|
|
64
|
+
allow_paid_integrations_under_budget: false,
|
|
65
|
+
default_auth_model: 'passwordless_email',
|
|
66
|
+
default_data_store: 'sqlite',
|
|
67
|
+
default_stack: 'node_typescript',
|
|
68
|
+
preapproved_integrations: [],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
// ─── Config CRUD ─────────────────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Read config.json. Returns empty object if missing or invalid.
|
|
74
|
+
*/
|
|
75
|
+
function readConfig(configPath) {
|
|
76
|
+
try {
|
|
77
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
78
|
+
const parsed = JSON.parse(content);
|
|
79
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
return parsed;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Deep merge helper: merge source into target, source values take precedence.
|
|
90
|
+
* Only merges plain objects recursively; arrays and primitives are replaced.
|
|
91
|
+
*/
|
|
92
|
+
function deepMerge(target, source) {
|
|
93
|
+
const result = { ...target };
|
|
94
|
+
for (const key of Object.keys(source)) {
|
|
95
|
+
const sourceVal = source[key];
|
|
96
|
+
const targetVal = result[key];
|
|
97
|
+
if (typeof sourceVal === 'object' &&
|
|
98
|
+
sourceVal !== null &&
|
|
99
|
+
!Array.isArray(sourceVal) &&
|
|
100
|
+
typeof targetVal === 'object' &&
|
|
101
|
+
targetVal !== null &&
|
|
102
|
+
!Array.isArray(targetVal)) {
|
|
103
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
result[key] = sourceVal;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Merge GSWD config into config.json under the `gswd` key.
|
|
113
|
+
* - Reads existing config (preserves ALL non-gswd keys)
|
|
114
|
+
* - Deep merges: existing gswd values take precedence over defaults
|
|
115
|
+
* - Fills in missing defaults
|
|
116
|
+
* - Writes back atomically
|
|
117
|
+
*
|
|
118
|
+
* CRITICAL: Never touches any key outside `gswd`.
|
|
119
|
+
*/
|
|
120
|
+
function mergeGswdConfig(configPath, gswdOverrides) {
|
|
121
|
+
// Read existing config (preserves GSD keys, etc.)
|
|
122
|
+
const existing = readConfig(configPath);
|
|
123
|
+
// Start with defaults
|
|
124
|
+
let mergedGswd = { ...exports.GSWD_CONFIG_DEFAULTS };
|
|
125
|
+
// Apply overrides on top of defaults
|
|
126
|
+
if (gswdOverrides) {
|
|
127
|
+
mergedGswd = deepMerge(mergedGswd, gswdOverrides);
|
|
128
|
+
}
|
|
129
|
+
// Apply existing user values on top (user values take precedence)
|
|
130
|
+
const existingGswd = existing.gswd;
|
|
131
|
+
if (typeof existingGswd === 'object' && existingGswd !== null && !Array.isArray(existingGswd)) {
|
|
132
|
+
mergedGswd = deepMerge(mergedGswd, existingGswd);
|
|
133
|
+
}
|
|
134
|
+
// Write back: existing config with updated gswd key
|
|
135
|
+
existing.gswd = mergedGswd;
|
|
136
|
+
(0, state_js_1.safeWriteJson)(configPath, existing);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get GSWD config with all defaults filled in for missing fields.
|
|
140
|
+
*/
|
|
141
|
+
function getGswdConfig(configPath) {
|
|
142
|
+
const config = readConfig(configPath);
|
|
143
|
+
const gswdRaw = config.gswd;
|
|
144
|
+
if (typeof gswdRaw !== 'object' || gswdRaw === null || Array.isArray(gswdRaw)) {
|
|
145
|
+
return { ...exports.GSWD_CONFIG_DEFAULTS };
|
|
146
|
+
}
|
|
147
|
+
// Merge defaults under existing values
|
|
148
|
+
const merged = deepMerge({ ...exports.GSWD_CONFIG_DEFAULTS }, gswdRaw);
|
|
149
|
+
return merged;
|
|
150
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSWD Imagine Agents Module — Agent definitions, orchestration, and result collection
|
|
3
|
+
*
|
|
4
|
+
* Provides batched parallel agent spawning that respects max_parallel_agents config.
|
|
5
|
+
* Agent definitions describe the 5 research agents from GSWD_SPEC Section 11.1.
|
|
6
|
+
* Actual Task() integration happens in the workflow orchestrator (imagine.ts).
|
|
7
|
+
*
|
|
8
|
+
* Schema: GSWD_SPEC.md Section 8.2 Steps 3-4, Section 11.1
|
|
9
|
+
*/
|
|
10
|
+
import type { StarterBrief } from './imagine-input.js';
|
|
11
|
+
export interface AgentDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
definitionPath: string;
|
|
14
|
+
outputArtifact: string;
|
|
15
|
+
requiredHeadings: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface AgentResult {
|
|
18
|
+
agent: string;
|
|
19
|
+
content: string;
|
|
20
|
+
status: 'complete' | 'failed';
|
|
21
|
+
error?: string;
|
|
22
|
+
duration_ms?: number;
|
|
23
|
+
}
|
|
24
|
+
export type SpawnFn = (prompt: string) => Promise<string>;
|
|
25
|
+
export type OnAgentComplete = (result: {
|
|
26
|
+
agent: string;
|
|
27
|
+
headline: string;
|
|
28
|
+
status: 'complete' | 'failed';
|
|
29
|
+
}) => void;
|
|
30
|
+
/**
|
|
31
|
+
* The 5 research agents for the Imagine stage.
|
|
32
|
+
* Names match GSWD_SPEC Section 11.1 file names.
|
|
33
|
+
*/
|
|
34
|
+
export declare const IMAGINE_AGENTS: AgentDefinition[];
|
|
35
|
+
/**
|
|
36
|
+
* Build the prompt that will be passed to Task() for an agent.
|
|
37
|
+
*
|
|
38
|
+
* Reads agent definition file and injects StarterBrief as context.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildAgentPrompt(agent: AgentDefinition, brief: StarterBrief, previousOutput?: string, userFeedback?: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Orchestrate agents in batches respecting maxParallel concurrency limit.
|
|
43
|
+
*
|
|
44
|
+
* Splits agents into batches of maxParallel size, executes each batch
|
|
45
|
+
* concurrently, collects results. Failed agents are marked with status: 'failed'
|
|
46
|
+
* but do not crash the orchestration.
|
|
47
|
+
*
|
|
48
|
+
* @param agents - Array of agent definitions to spawn
|
|
49
|
+
* @param maxParallel - Maximum concurrent agents (from config.max_parallel_agents)
|
|
50
|
+
* @param brief - StarterBrief input for all agents
|
|
51
|
+
* @param spawnFn - Function that spawns an agent (Task() wrapper)
|
|
52
|
+
* @returns All agent results including failures
|
|
53
|
+
*/
|
|
54
|
+
export declare function orchestrateAgents(agents: AgentDefinition[], maxParallel: number, brief: StarterBrief, spawnFn: SpawnFn, previousOutputs?: Record<string, string>, userFeedback?: string, onComplete?: OnAgentComplete): Promise<AgentResult[]>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GSWD Imagine Agents Module — Agent definitions, orchestration, and result collection
|
|
4
|
+
*
|
|
5
|
+
* Provides batched parallel agent spawning that respects max_parallel_agents config.
|
|
6
|
+
* Agent definitions describe the 5 research agents from GSWD_SPEC Section 11.1.
|
|
7
|
+
* Actual Task() integration happens in the workflow orchestrator (imagine.ts).
|
|
8
|
+
*
|
|
9
|
+
* Schema: GSWD_SPEC.md Section 8.2 Steps 3-4, Section 11.1
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.IMAGINE_AGENTS = void 0;
|
|
46
|
+
exports.buildAgentPrompt = buildAgentPrompt;
|
|
47
|
+
exports.orchestrateAgents = orchestrateAgents;
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
49
|
+
const render_js_1 = require("./render.js");
|
|
50
|
+
// ─── Agent Definitions ───────────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* The 5 research agents for the Imagine stage.
|
|
53
|
+
* Names match GSWD_SPEC Section 11.1 file names.
|
|
54
|
+
*/
|
|
55
|
+
exports.IMAGINE_AGENTS = [
|
|
56
|
+
{
|
|
57
|
+
name: 'market-researcher',
|
|
58
|
+
definitionPath: 'agents/gswd/market-researcher.md',
|
|
59
|
+
outputArtifact: 'COMPETITION.md',
|
|
60
|
+
requiredHeadings: ['## Market Overview', '## Competitors'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'icp-persona',
|
|
64
|
+
definitionPath: 'agents/gswd/icp-persona.md',
|
|
65
|
+
outputArtifact: 'ICP.md',
|
|
66
|
+
requiredHeadings: ['## ICP Profile', '## Pain Points'],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'positioning',
|
|
70
|
+
definitionPath: 'agents/gswd/positioning.md',
|
|
71
|
+
outputArtifact: 'GTM.md',
|
|
72
|
+
requiredHeadings: ['## Value Proposition'],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'brainstorm-alternatives',
|
|
76
|
+
definitionPath: 'agents/gswd/brainstorm-alternatives.md',
|
|
77
|
+
outputArtifact: 'DIRECTIONS',
|
|
78
|
+
requiredHeadings: ['## Direction 1', '## Direction 2', '## Direction 3'],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'devils-advocate',
|
|
82
|
+
definitionPath: 'agents/gswd/devils-advocate.md',
|
|
83
|
+
outputArtifact: 'RISKS',
|
|
84
|
+
requiredHeadings: ['## Risks'],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
// ─── Prompt Building ─────────────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Build the prompt that will be passed to Task() for an agent.
|
|
90
|
+
*
|
|
91
|
+
* Reads agent definition file and injects StarterBrief as context.
|
|
92
|
+
*/
|
|
93
|
+
function buildAgentPrompt(agent, brief, previousOutput, userFeedback) {
|
|
94
|
+
let definition;
|
|
95
|
+
try {
|
|
96
|
+
definition = fs.readFileSync(agent.definitionPath, 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
definition = `Agent: ${agent.name}\nRole: Research agent for ${agent.outputArtifact}`;
|
|
100
|
+
}
|
|
101
|
+
const briefJson = JSON.stringify(brief, null, 2);
|
|
102
|
+
let prompt = `${definition}
|
|
103
|
+
|
|
104
|
+
<starter_brief>
|
|
105
|
+
${briefJson}
|
|
106
|
+
</starter_brief>`;
|
|
107
|
+
// Re-run augmentation (IMAGINE-03): include previous output + user feedback
|
|
108
|
+
if (previousOutput || userFeedback) {
|
|
109
|
+
prompt += '\n\n<rerun_context>';
|
|
110
|
+
if (previousOutput) {
|
|
111
|
+
prompt += `\n\n## Previous Output\nYour previous research produced the following findings. Build on these — do not start from scratch unless the user feedback explicitly asks for a different direction.\n\n${previousOutput}`;
|
|
112
|
+
}
|
|
113
|
+
if (userFeedback) {
|
|
114
|
+
prompt += `\n\n## User Feedback\nThe user was not satisfied with the previous directions and provided this feedback. Use it to refine your research:\n\n${userFeedback}`;
|
|
115
|
+
}
|
|
116
|
+
prompt += '\n</rerun_context>';
|
|
117
|
+
}
|
|
118
|
+
prompt += `\n\nProduce your output now. Include all required headings: ${agent.requiredHeadings.join(', ')}.`;
|
|
119
|
+
return prompt;
|
|
120
|
+
}
|
|
121
|
+
// ─── Orchestration ───────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Orchestrate agents in batches respecting maxParallel concurrency limit.
|
|
124
|
+
*
|
|
125
|
+
* Splits agents into batches of maxParallel size, executes each batch
|
|
126
|
+
* concurrently, collects results. Failed agents are marked with status: 'failed'
|
|
127
|
+
* but do not crash the orchestration.
|
|
128
|
+
*
|
|
129
|
+
* @param agents - Array of agent definitions to spawn
|
|
130
|
+
* @param maxParallel - Maximum concurrent agents (from config.max_parallel_agents)
|
|
131
|
+
* @param brief - StarterBrief input for all agents
|
|
132
|
+
* @param spawnFn - Function that spawns an agent (Task() wrapper)
|
|
133
|
+
* @returns All agent results including failures
|
|
134
|
+
*/
|
|
135
|
+
async function orchestrateAgents(agents, maxParallel, brief, spawnFn, previousOutputs, userFeedback, onComplete) {
|
|
136
|
+
const results = [];
|
|
137
|
+
// Split into batches
|
|
138
|
+
for (let i = 0; i < agents.length; i += maxParallel) {
|
|
139
|
+
const batch = agents.slice(i, i + maxParallel);
|
|
140
|
+
// Execute batch in parallel
|
|
141
|
+
const batchPromises = batch.map(async (agent) => {
|
|
142
|
+
const start = Date.now();
|
|
143
|
+
try {
|
|
144
|
+
const previousOutput = previousOutputs?.[agent.name];
|
|
145
|
+
const prompt = buildAgentPrompt(agent, brief, previousOutput, userFeedback);
|
|
146
|
+
const content = await spawnFn(prompt);
|
|
147
|
+
const result = {
|
|
148
|
+
agent: agent.name,
|
|
149
|
+
content,
|
|
150
|
+
status: 'complete',
|
|
151
|
+
duration_ms: Date.now() - start,
|
|
152
|
+
};
|
|
153
|
+
if (onComplete) {
|
|
154
|
+
onComplete({
|
|
155
|
+
agent: agent.name,
|
|
156
|
+
headline: (0, render_js_1.extractHeadline)(result.content, agent.name),
|
|
157
|
+
status: result.status,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
164
|
+
const result = {
|
|
165
|
+
agent: agent.name,
|
|
166
|
+
content: '',
|
|
167
|
+
status: 'failed',
|
|
168
|
+
error: message,
|
|
169
|
+
duration_ms: Date.now() - start,
|
|
170
|
+
};
|
|
171
|
+
if (onComplete) {
|
|
172
|
+
onComplete({
|
|
173
|
+
agent: agent.name,
|
|
174
|
+
headline: message || 'Agent failed',
|
|
175
|
+
status: 'failed',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
const batchResults = await Promise.all(batchPromises);
|
|
182
|
+
results.push(...batchResults);
|
|
183
|
+
}
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSWD Imagine Gate Module — Decision gate validation for DECISIONS.md
|
|
3
|
+
*
|
|
4
|
+
* Pure function: validates DECISIONS.md against hard gate requirements.
|
|
5
|
+
* No side effects, no file writes, no state mutations.
|
|
6
|
+
*
|
|
7
|
+
* Gate requirements (GSWD_SPEC Section 3.3, Appendix A1):
|
|
8
|
+
* - ## Frozen Decisions: >= 8 items
|
|
9
|
+
* - ## Success Metrics: 1-3 items
|
|
10
|
+
* - ## Out of Scope: >= 1 item
|
|
11
|
+
* - ## Risks & Mitigations: >= 5 items
|
|
12
|
+
* - ## Open Questions: >= 0 items (section must exist)
|
|
13
|
+
*/
|
|
14
|
+
export interface InsufficientCount {
|
|
15
|
+
section: string;
|
|
16
|
+
required: number;
|
|
17
|
+
actual: number;
|
|
18
|
+
constraint?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GateResult {
|
|
21
|
+
passed: boolean;
|
|
22
|
+
missing_sections: string[];
|
|
23
|
+
insufficient_counts: InsufficientCount[];
|
|
24
|
+
summary: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Count list items in a section.
|
|
28
|
+
*
|
|
29
|
+
* Counts lines starting with:
|
|
30
|
+
* - `- ` (bullet)
|
|
31
|
+
* - `* ` (bullet)
|
|
32
|
+
* - `N. ` or `N) ` (numbered)
|
|
33
|
+
* - `### ` (sub-headings as items)
|
|
34
|
+
*
|
|
35
|
+
* Returns 0 for empty/null/undefined content.
|
|
36
|
+
*/
|
|
37
|
+
export declare function countListItems(content: string | null | undefined): number;
|
|
38
|
+
/**
|
|
39
|
+
* Validate DECISIONS.md against the hard gate requirements.
|
|
40
|
+
*
|
|
41
|
+
* Step 1: Required headings (via parse.ts validateHeadings)
|
|
42
|
+
* Step 2: Item counts per section (via parse.ts extractHeadingContent + countListItems)
|
|
43
|
+
* Step 3: Build structured result
|
|
44
|
+
*
|
|
45
|
+
* Returns: GateResult with passed/failed status, missing sections, and count issues.
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateDecisionGate(decisionsContent: string): GateResult;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GSWD Imagine Gate Module — Decision gate validation for DECISIONS.md
|
|
4
|
+
*
|
|
5
|
+
* Pure function: validates DECISIONS.md against hard gate requirements.
|
|
6
|
+
* No side effects, no file writes, no state mutations.
|
|
7
|
+
*
|
|
8
|
+
* Gate requirements (GSWD_SPEC Section 3.3, Appendix A1):
|
|
9
|
+
* - ## Frozen Decisions: >= 8 items
|
|
10
|
+
* - ## Success Metrics: 1-3 items
|
|
11
|
+
* - ## Out of Scope: >= 1 item
|
|
12
|
+
* - ## Risks & Mitigations: >= 5 items
|
|
13
|
+
* - ## Open Questions: >= 0 items (section must exist)
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.countListItems = countListItems;
|
|
17
|
+
exports.validateDecisionGate = validateDecisionGate;
|
|
18
|
+
const parse_js_1 = require("./parse.js");
|
|
19
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Count list items in a section.
|
|
22
|
+
*
|
|
23
|
+
* Counts lines starting with:
|
|
24
|
+
* - `- ` (bullet)
|
|
25
|
+
* - `* ` (bullet)
|
|
26
|
+
* - `N. ` or `N) ` (numbered)
|
|
27
|
+
* - `### ` (sub-headings as items)
|
|
28
|
+
*
|
|
29
|
+
* Returns 0 for empty/null/undefined content.
|
|
30
|
+
*/
|
|
31
|
+
function countListItems(content) {
|
|
32
|
+
if (!content)
|
|
33
|
+
return 0;
|
|
34
|
+
const lines = content.split('\n');
|
|
35
|
+
let count = 0;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
const trimmed = line.trim();
|
|
38
|
+
if (/^[-*]\s+/.test(trimmed)) {
|
|
39
|
+
count++;
|
|
40
|
+
}
|
|
41
|
+
else if (/^\d+[.)]\s+/.test(trimmed)) {
|
|
42
|
+
count++;
|
|
43
|
+
}
|
|
44
|
+
else if (/^###\s+/.test(trimmed)) {
|
|
45
|
+
count++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return count;
|
|
49
|
+
}
|
|
50
|
+
// ─── Gate Validation ─────────────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* Validate DECISIONS.md against the hard gate requirements.
|
|
53
|
+
*
|
|
54
|
+
* Step 1: Required headings (via parse.ts validateHeadings)
|
|
55
|
+
* Step 2: Item counts per section (via parse.ts extractHeadingContent + countListItems)
|
|
56
|
+
* Step 3: Build structured result
|
|
57
|
+
*
|
|
58
|
+
* Returns: GateResult with passed/failed status, missing sections, and count issues.
|
|
59
|
+
*/
|
|
60
|
+
function validateDecisionGate(decisionsContent) {
|
|
61
|
+
// Step 1: Required headings
|
|
62
|
+
const headingResult = (0, parse_js_1.validateHeadings)(decisionsContent, 'DECISIONS.md');
|
|
63
|
+
// Step 2: Count items per section
|
|
64
|
+
const insufficient = [];
|
|
65
|
+
// Frozen Decisions: >= 8
|
|
66
|
+
const frozenSection = (0, parse_js_1.extractHeadingContent)(decisionsContent, '## Frozen Decisions');
|
|
67
|
+
const frozenCount = countListItems(frozenSection);
|
|
68
|
+
if (frozenCount < 8) {
|
|
69
|
+
insufficient.push({
|
|
70
|
+
section: 'Frozen Decisions',
|
|
71
|
+
required: 8,
|
|
72
|
+
actual: frozenCount,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Success Metrics: 1-3
|
|
76
|
+
const metricsSection = (0, parse_js_1.extractHeadingContent)(decisionsContent, '## Success Metrics');
|
|
77
|
+
const metricsCount = countListItems(metricsSection);
|
|
78
|
+
if (metricsCount < 1 || metricsCount > 3) {
|
|
79
|
+
insufficient.push({
|
|
80
|
+
section: 'Success Metrics',
|
|
81
|
+
required: metricsCount < 1 ? 1 : 3,
|
|
82
|
+
actual: metricsCount,
|
|
83
|
+
constraint: '1-3',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Out of Scope: >= 1
|
|
87
|
+
const outOfScopeSection = (0, parse_js_1.extractHeadingContent)(decisionsContent, '## Out of Scope');
|
|
88
|
+
const outOfScopeCount = countListItems(outOfScopeSection);
|
|
89
|
+
if (outOfScopeCount < 1) {
|
|
90
|
+
insufficient.push({
|
|
91
|
+
section: 'Out of Scope',
|
|
92
|
+
required: 1,
|
|
93
|
+
actual: outOfScopeCount,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Risks & Mitigations: >= 5
|
|
97
|
+
const risksSection = (0, parse_js_1.extractHeadingContent)(decisionsContent, '## Risks & Mitigations');
|
|
98
|
+
const risksCount = countListItems(risksSection);
|
|
99
|
+
if (risksCount < 5) {
|
|
100
|
+
insufficient.push({
|
|
101
|
+
section: 'Risks & Mitigations',
|
|
102
|
+
required: 5,
|
|
103
|
+
actual: risksCount,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Open Questions: >= 0 (section must exist, but can be empty)
|
|
107
|
+
// No count check needed — heading presence is sufficient
|
|
108
|
+
// Step 3: Build result
|
|
109
|
+
const passed = headingResult.valid && insufficient.length === 0;
|
|
110
|
+
const totalIssues = headingResult.missing.length + insufficient.length;
|
|
111
|
+
let summary;
|
|
112
|
+
if (passed) {
|
|
113
|
+
summary = 'PASS: All 5 sections present, counts valid';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const issues = [];
|
|
117
|
+
if (headingResult.missing.length > 0) {
|
|
118
|
+
issues.push(`${headingResult.missing.length} missing heading(s)`);
|
|
119
|
+
}
|
|
120
|
+
if (insufficient.length > 0) {
|
|
121
|
+
issues.push(`${insufficient.length} insufficient count(s)`);
|
|
122
|
+
}
|
|
123
|
+
summary = `FAIL: ${totalIssues} issue(s) — ${issues.join(', ')}`;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
passed,
|
|
127
|
+
missing_sections: headingResult.missing,
|
|
128
|
+
insufficient_counts: insufficient,
|
|
129
|
+
summary,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSWD Imagine Input Module — Input collection with file parsing and intake building
|
|
3
|
+
*
|
|
4
|
+
* Two input paths converge to a single StarterBrief interface:
|
|
5
|
+
* 1. parseIdeaFile: Parse @idea.md into a starter brief
|
|
6
|
+
* 2. buildFromIntake: Build from 3-question intake answers
|
|
7
|
+
*
|
|
8
|
+
* Schema: GSWD_SPEC.md Section 8.2, Steps 1-2
|
|
9
|
+
*/
|
|
10
|
+
export interface StarterBrief {
|
|
11
|
+
vision: string;
|
|
12
|
+
target_user: string;
|
|
13
|
+
why_now: string;
|
|
14
|
+
raw_themes: string[];
|
|
15
|
+
source: 'file' | 'intake';
|
|
16
|
+
}
|
|
17
|
+
export interface IntakeAnswers {
|
|
18
|
+
vision: string;
|
|
19
|
+
user: string;
|
|
20
|
+
whyNow: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ValidationResult {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
missing: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse an @idea.md file into a StarterBrief.
|
|
28
|
+
*
|
|
29
|
+
* Extracts vision, target user, timing, and themes from freeform markdown.
|
|
30
|
+
* Falls back to first 200 chars if specific fields can't be extracted.
|
|
31
|
+
* Throws on empty or non-existent files.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseIdeaFile(filePath: string): StarterBrief;
|
|
34
|
+
/**
|
|
35
|
+
* Build a StarterBrief from 3-question intake answers.
|
|
36
|
+
*
|
|
37
|
+
* Maps: vision -> vision, user -> target_user, whyNow -> why_now.
|
|
38
|
+
* Extracts themes by splitting answers on delimiters.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildFromIntake(answers: IntakeAnswers): StarterBrief;
|
|
41
|
+
/**
|
|
42
|
+
* Validate that a StarterBrief has sufficient signal for research agents.
|
|
43
|
+
*
|
|
44
|
+
* Checks: vision > 10 chars, target_user > 5 chars, why_now > 5 chars, raw_themes >= 1.
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateBrief(brief: StarterBrief): ValidationResult;
|