claude-all-hands 1.0.1 → 1.0.3
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/.claude/agents/code-simplifier.md +52 -0
- package/.claude/agents/curator.md +186 -246
- package/.claude/agents/documentation-taxonomist.md +255 -0
- package/.claude/agents/documentation-writer.md +366 -0
- package/.claude/agents/planner.md +123 -166
- package/.claude/agents/researcher.md +58 -41
- package/.claude/agents/surveyor.md +81 -0
- package/.claude/agents/worker.md +74 -0
- package/.claude/commands/continue.md +122 -0
- package/.claude/commands/create-skill.md +107 -0
- package/.claude/commands/create-specialist.md +111 -0
- package/.claude/commands/curator-audit.md +4 -0
- package/.claude/commands/debug.md +183 -0
- package/.claude/commands/docs-adjust.md +214 -0
- package/.claude/commands/docs-audit.md +172 -0
- package/.claude/commands/docs-init.md +210 -0
- package/.claude/commands/plan.md +199 -102
- package/.claude/commands/validate.md +11 -0
- package/.claude/commands/whats-next.md +106 -134
- package/.claude/envoy/README.md +5 -5
- package/.claude/envoy/envoy +11 -14
- package/.claude/envoy/package-lock.json +1594 -0
- package/.claude/envoy/package.json +38 -0
- package/.claude/envoy/src/cli.ts +126 -0
- package/.claude/envoy/src/commands/base.ts +216 -0
- package/.claude/envoy/src/commands/docs.ts +881 -0
- package/.claude/envoy/src/commands/gemini.ts +999 -0
- package/.claude/envoy/src/commands/git.ts +639 -0
- package/.claude/envoy/src/commands/index.ts +73 -0
- package/.claude/envoy/src/commands/knowledge.ts +178 -0
- package/.claude/envoy/src/commands/perplexity.ts +129 -0
- package/.claude/envoy/src/commands/plan/core.ts +134 -0
- package/.claude/envoy/src/commands/plan/findings.ts +446 -0
- package/.claude/envoy/src/commands/plan/gates.ts +672 -0
- package/.claude/envoy/src/commands/plan/index.ts +135 -0
- package/.claude/envoy/src/commands/plan/lifecycle.ts +648 -0
- package/.claude/envoy/src/commands/plan/plan-file.ts +138 -0
- package/.claude/envoy/src/commands/plan/prompts.ts +285 -0
- package/.claude/envoy/src/commands/plan/protocols.ts +166 -0
- package/.claude/envoy/src/commands/repomix.ts +99 -0
- package/.claude/envoy/src/commands/tavily.ts +220 -0
- package/.claude/envoy/src/commands/xai.ts +168 -0
- package/.claude/envoy/src/lib/ast-queries.ts +261 -0
- package/.claude/envoy/src/lib/design.ts +41 -0
- package/.claude/envoy/src/lib/feedback-schemas.ts +154 -0
- package/.claude/envoy/src/lib/findings.ts +215 -0
- package/.claude/envoy/src/lib/gates.ts +572 -0
- package/.claude/envoy/src/lib/git.ts +132 -0
- package/.claude/envoy/src/lib/index.ts +188 -0
- package/.claude/envoy/src/lib/knowledge.ts +646 -0
- package/.claude/envoy/src/lib/markdown.ts +75 -0
- package/.claude/envoy/src/lib/observability.ts +262 -0
- package/.claude/envoy/src/lib/paths.ts +130 -0
- package/.claude/envoy/src/lib/plan-io.ts +117 -0
- package/.claude/envoy/src/lib/prompts.ts +231 -0
- package/.claude/envoy/src/lib/protocols.ts +314 -0
- package/.claude/envoy/src/lib/repomix.ts +133 -0
- package/.claude/envoy/src/lib/retry.ts +138 -0
- package/.claude/envoy/src/lib/tree-sitter-utils.ts +301 -0
- package/.claude/envoy/src/lib/watcher.ts +167 -0
- package/.claude/envoy/src/types/tree-sitter.d.ts +76 -0
- package/.claude/envoy/tsconfig.json +21 -0
- package/.claude/hooks/scripts/enforce_research_fetch.py +1 -1
- package/.claude/hooks/scripts/scan_agents.py +62 -0
- package/.claude/hooks/scripts/scan_commands.py +50 -0
- package/.claude/hooks/scripts/scan_skills.py +46 -70
- package/.claude/hooks/scripts/validate_artifacts.py +128 -0
- package/.claude/hooks/startup.sh +26 -24
- package/.claude/protocols/bug-discovery.yaml +55 -0
- package/.claude/protocols/debugging.yaml +51 -0
- package/.claude/protocols/discovery.yaml +53 -0
- package/.claude/protocols/implementation.yaml +84 -0
- package/.claude/settings.json +38 -97
- package/.claude/skills/brainstorming/SKILL.md +54 -0
- package/.claude/skills/commands-development/SKILL.md +630 -0
- package/.claude/skills/commands-development/references/arguments.md +252 -0
- package/.claude/skills/commands-development/references/patterns.md +796 -0
- package/.claude/skills/commands-development/references/tool-restrictions.md +376 -0
- package/.claude/skills/discovery-mode/SKILL.md +108 -0
- package/.claude/skills/documentation-taxonomy/SKILL.md +287 -0
- package/.claude/skills/hooks-development/SKILL.md +332 -0
- package/.claude/skills/hooks-development/references/command-vs-prompt.md +269 -0
- package/.claude/skills/hooks-development/references/examples.md +658 -0
- package/.claude/skills/hooks-development/references/hook-types.md +463 -0
- package/.claude/skills/hooks-development/references/input-output-schemas.md +469 -0
- package/.claude/skills/hooks-development/references/matchers.md +470 -0
- package/.claude/skills/hooks-development/references/troubleshooting.md +587 -0
- package/.claude/skills/implementation-mode/SKILL.md +171 -0
- package/.claude/skills/knowledge-discovery/SKILL.md +178 -0
- package/.claude/skills/research-tools/SKILL.md +35 -33
- package/.claude/skills/skills-development/SKILL.md +192 -0
- package/.claude/skills/skills-development/references/api-security.md +226 -0
- package/.claude/skills/skills-development/references/be-clear-and-direct.md +531 -0
- package/.claude/skills/skills-development/references/common-patterns.md +595 -0
- package/.claude/skills/skills-development/references/core-principles.md +437 -0
- package/.claude/skills/skills-development/references/executable-code.md +175 -0
- package/.claude/skills/skills-development/references/iteration-and-testing.md +474 -0
- package/.claude/skills/skills-development/references/recommended-structure.md +168 -0
- package/.claude/skills/skills-development/references/skill-structure.md +372 -0
- package/.claude/skills/skills-development/references/use-xml-tags.md +466 -0
- package/.claude/skills/skills-development/references/using-scripts.md +113 -0
- package/.claude/skills/skills-development/references/using-templates.md +112 -0
- package/.claude/skills/skills-development/references/workflows-and-validation.md +510 -0
- package/.claude/skills/skills-development/templates/router-skill.md +73 -0
- package/.claude/skills/skills-development/templates/simple-skill.md +33 -0
- package/.claude/skills/skills-development/workflows/add-reference.md +96 -0
- package/.claude/skills/skills-development/workflows/add-script.md +93 -0
- package/.claude/skills/skills-development/workflows/add-template.md +74 -0
- package/.claude/skills/skills-development/workflows/add-workflow.md +120 -0
- package/.claude/skills/skills-development/workflows/audit-skill.md +138 -0
- package/.claude/skills/skills-development/workflows/create-domain-expertise-skill.md +605 -0
- package/.claude/skills/skills-development/workflows/create-new-skill.md +191 -0
- package/.claude/skills/skills-development/workflows/get-guidance.md +121 -0
- package/.claude/skills/skills-development/workflows/upgrade-to-router.md +161 -0
- package/.claude/skills/skills-development/workflows/verify-skill.md +204 -0
- package/.claude/skills/subagents-development/SKILL.md +325 -0
- package/.claude/skills/subagents-development/references/context-management.md +567 -0
- package/.claude/skills/subagents-development/references/debugging-agents.md +714 -0
- package/.claude/skills/subagents-development/references/error-handling-and-recovery.md +502 -0
- package/.claude/skills/subagents-development/references/evaluation-and-testing.md +374 -0
- package/.claude/skills/subagents-development/references/orchestration-patterns.md +591 -0
- package/.claude/skills/subagents-development/references/subagents.md +508 -0
- package/.claude/skills/subagents-development/references/writing-subagent-prompts.md +517 -0
- package/.claude/statusline.sh +24 -0
- package/bin/cli.js +150 -72
- package/package.json +1 -1
- package/.claude/agents/explorer.md +0 -62
- package/.claude/agents/parallel-worker.md +0 -121
- package/.claude/commands/curation-fix.md +0 -92
- package/.claude/commands/new-branch.md +0 -36
- package/.claude/commands/parallel-discovery.md +0 -69
- package/.claude/commands/parallel-orchestration.md +0 -99
- package/.claude/commands/plan-checkpoint.md +0 -37
- package/.claude/envoy/commands/__init__.py +0 -1
- package/.claude/envoy/commands/base.py +0 -95
- package/.claude/envoy/commands/parallel.py +0 -439
- package/.claude/envoy/commands/perplexity.py +0 -86
- package/.claude/envoy/commands/plans.py +0 -451
- package/.claude/envoy/commands/tavily.py +0 -156
- package/.claude/envoy/commands/vertex.py +0 -358
- package/.claude/envoy/commands/xai.py +0 -124
- package/.claude/envoy/envoy.py +0 -122
- package/.claude/envoy/pyrightconfig.json +0 -4
- package/.claude/envoy/requirements.txt +0 -2
- package/.claude/hooks/capture-queries.sh +0 -3
- package/.claude/hooks/scripts/enforce_planning.py +0 -118
- package/.claude/hooks/scripts/enforce_rg.py +0 -34
- package/.claude/hooks/scripts/validate_skill.py +0 -81
- package/.claude/skills/claude-envoy-curation/SKILL.md +0 -162
- package/.claude/skills/claude-envoy-usage/SKILL.md +0 -46
- package/.claude/skills/command-development/SKILL.md +0 -206
- package/.claude/skills/command-development/examples/simple-commands.md +0 -212
- package/.claude/skills/command-development/references/frontmatter-reference.md +0 -221
- package/.claude/skills/hook-development/SKILL.md +0 -127
- package/.claude/skills/hook-development/examples/command-hooks.md +0 -301
- package/.claude/skills/hook-development/examples/prompt-hooks.md +0 -114
- package/.claude/skills/hook-development/references/event-reference.md +0 -226
- package/.claude/skills/repomix-extraction/SKILL.md +0 -91
- package/.claude/skills/skill-development/SKILL.md +0 -168
- package/.claude/skills/skill-development/examples/complete-skill-examples.md +0 -281
- package/.claude/skills/skill-development/references/progressive-disclosure.md +0 -141
- package/.claude/skills/skill-development/references/writing-style.md +0 -180
- package/.claude/skills/skill-development/scripts/validate-skill.sh +0 -144
- package/.claude/skills/specialist-builder/SKILL.md +0 -327
- package/.claude/skills/specialist-builder/docs/agent-catalog.md +0 -28
- package/.claude/skills/specialist-builder/examples/complete-agent-examples.md +0 -206
- package/.claude/skills/specialist-builder/references/system-prompt-patterns.md +0 -281
- package/.claude/skills/specialist-builder/references/triggering-examples.md +0 -162
- package/.claude/skills/specialist-builder/scripts/validate-agent.sh +0 -137
- /package/.claude/{envoy/claude-envoy.py → skills/claude-envoy-patterns/SKILL.md} +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability system for claude-envoy.
|
|
3
|
+
* Dual system: metrics.jsonl (analytics) + envoy.log (detailed traces).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { appendFileSync, mkdirSync, existsSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { getProjectRoot, getBranch } from "./git.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Derive plan_name from branch.
|
|
12
|
+
* Worktree branches follow pattern: feature-foo/implementation-1-A
|
|
13
|
+
* Plan name is the parent branch before /implementation-*
|
|
14
|
+
*/
|
|
15
|
+
export function getPlanName(branch?: string | null): string | undefined {
|
|
16
|
+
const b = branch ?? getBranch();
|
|
17
|
+
if (!b) return undefined;
|
|
18
|
+
// Strip /implementation-* suffix if present
|
|
19
|
+
const match = b.match(/^(.+?)\/implementation-/);
|
|
20
|
+
return match ? match[1] : b;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// --- Types ---
|
|
24
|
+
|
|
25
|
+
export type LogLevel = "info" | "warn" | "error";
|
|
26
|
+
|
|
27
|
+
export interface LogEntry {
|
|
28
|
+
timestamp: string;
|
|
29
|
+
level: LogLevel;
|
|
30
|
+
command: string;
|
|
31
|
+
plan_name?: string;
|
|
32
|
+
branch?: string;
|
|
33
|
+
caller?: string;
|
|
34
|
+
args?: Record<string, unknown>;
|
|
35
|
+
result?: "success" | "error";
|
|
36
|
+
duration_ms?: number;
|
|
37
|
+
context?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MetricEvent {
|
|
41
|
+
type: string;
|
|
42
|
+
timestamp: string;
|
|
43
|
+
plan_name?: string;
|
|
44
|
+
branch?: string;
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- File Paths ---
|
|
49
|
+
|
|
50
|
+
function getObservabilityDir(): string {
|
|
51
|
+
return join(getProjectRoot(), ".claude");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getLogPath(): string {
|
|
55
|
+
return join(getObservabilityDir(), "envoy.log");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getMetricsPath(): string {
|
|
59
|
+
return join(getObservabilityDir(), "metrics.jsonl");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// --- Ensure Directory Exists ---
|
|
63
|
+
|
|
64
|
+
function ensureObservabilityDir(): void {
|
|
65
|
+
const dir = getObservabilityDir();
|
|
66
|
+
if (!existsSync(dir)) {
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Logging ---
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Write a log entry to envoy.log.
|
|
75
|
+
*/
|
|
76
|
+
export function log(entry: Omit<LogEntry, "timestamp">): void {
|
|
77
|
+
ensureObservabilityDir();
|
|
78
|
+
const branch = getBranch() || undefined;
|
|
79
|
+
const fullEntry: LogEntry = {
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
branch,
|
|
82
|
+
plan_name: getPlanName(branch),
|
|
83
|
+
...entry,
|
|
84
|
+
};
|
|
85
|
+
try {
|
|
86
|
+
appendFileSync(getLogPath(), JSON.stringify(fullEntry) + "\n");
|
|
87
|
+
} catch {
|
|
88
|
+
// Silent fail - observability should not break commands
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Log an info-level entry.
|
|
94
|
+
*/
|
|
95
|
+
export function logInfo(
|
|
96
|
+
command: string,
|
|
97
|
+
context?: Record<string, unknown>,
|
|
98
|
+
args?: Record<string, unknown>
|
|
99
|
+
): void {
|
|
100
|
+
log({ level: "info", command, context, args });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Log a warning-level entry.
|
|
105
|
+
*/
|
|
106
|
+
export function logWarn(
|
|
107
|
+
command: string,
|
|
108
|
+
context?: Record<string, unknown>,
|
|
109
|
+
args?: Record<string, unknown>
|
|
110
|
+
): void {
|
|
111
|
+
log({ level: "warn", command, context, args });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Log an error-level entry.
|
|
116
|
+
*/
|
|
117
|
+
export function logError(
|
|
118
|
+
command: string,
|
|
119
|
+
context?: Record<string, unknown>,
|
|
120
|
+
args?: Record<string, unknown>
|
|
121
|
+
): void {
|
|
122
|
+
log({ level: "error", command, context, args });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Log command start.
|
|
127
|
+
*/
|
|
128
|
+
export function logCommandStart(
|
|
129
|
+
command: string,
|
|
130
|
+
args?: Record<string, unknown>
|
|
131
|
+
): void {
|
|
132
|
+
log({
|
|
133
|
+
level: "info",
|
|
134
|
+
command,
|
|
135
|
+
args,
|
|
136
|
+
result: undefined,
|
|
137
|
+
context: { phase: "start" },
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Log command completion.
|
|
143
|
+
*/
|
|
144
|
+
export function logCommandComplete(
|
|
145
|
+
command: string,
|
|
146
|
+
result: "success" | "error",
|
|
147
|
+
duration_ms: number,
|
|
148
|
+
context?: Record<string, unknown>
|
|
149
|
+
): void {
|
|
150
|
+
log({
|
|
151
|
+
level: result === "error" ? "error" : "info",
|
|
152
|
+
command,
|
|
153
|
+
result,
|
|
154
|
+
duration_ms,
|
|
155
|
+
context: { ...context, phase: "complete" },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- Metrics ---
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Append a metric event to metrics.jsonl.
|
|
163
|
+
*/
|
|
164
|
+
export function recordMetric(
|
|
165
|
+
event: { type: string } & Record<string, unknown>
|
|
166
|
+
): void {
|
|
167
|
+
ensureObservabilityDir();
|
|
168
|
+
const branch = typeof event.branch === "string" ? event.branch : getBranch();
|
|
169
|
+
const planName = typeof event.plan_name === "string" ? event.plan_name : getPlanName(branch);
|
|
170
|
+
const fullEvent: MetricEvent = {
|
|
171
|
+
...event,
|
|
172
|
+
timestamp: new Date().toISOString(),
|
|
173
|
+
branch: branch || undefined,
|
|
174
|
+
plan_name: planName,
|
|
175
|
+
};
|
|
176
|
+
try {
|
|
177
|
+
appendFileSync(getMetricsPath(), JSON.stringify(fullEvent) + "\n");
|
|
178
|
+
} catch {
|
|
179
|
+
// Silent fail - observability should not break commands
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// --- Specific Metric Events ---
|
|
184
|
+
|
|
185
|
+
export function recordPlanCreated(data: {
|
|
186
|
+
mode: string;
|
|
187
|
+
prompt_count: number;
|
|
188
|
+
has_variants: boolean;
|
|
189
|
+
plan_name?: string;
|
|
190
|
+
}): void {
|
|
191
|
+
recordMetric({ type: "plan_created", ...data });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function recordPlanCompleted(data: {
|
|
195
|
+
duration_ms: number;
|
|
196
|
+
prompt_count: number;
|
|
197
|
+
total_iterations: number;
|
|
198
|
+
gemini_calls: number;
|
|
199
|
+
plan_name?: string;
|
|
200
|
+
}): void {
|
|
201
|
+
recordMetric({ type: "plan_completed", ...data });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function recordPromptStarted(data: {
|
|
205
|
+
prompt_num: number;
|
|
206
|
+
variant?: string | null;
|
|
207
|
+
specialist?: string;
|
|
208
|
+
is_debug: boolean;
|
|
209
|
+
plan_name?: string;
|
|
210
|
+
}): void {
|
|
211
|
+
recordMetric({ type: "prompt_started", ...data });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function recordPromptCompleted(data: {
|
|
215
|
+
prompt_num: number;
|
|
216
|
+
variant?: string | null;
|
|
217
|
+
duration_ms: number;
|
|
218
|
+
iterations: number;
|
|
219
|
+
review_passes: number;
|
|
220
|
+
plan_name?: string;
|
|
221
|
+
}): void {
|
|
222
|
+
recordMetric({ type: "prompt_completed", ...data });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function recordGateCompleted(data: {
|
|
226
|
+
gate_type: string;
|
|
227
|
+
duration_ms: number;
|
|
228
|
+
user_refinements_count: number;
|
|
229
|
+
plan_name?: string;
|
|
230
|
+
}): void {
|
|
231
|
+
recordMetric({ type: "gate_completed", ...data });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function recordGeminiCall(data: {
|
|
235
|
+
endpoint: "audit" | "review" | "ask";
|
|
236
|
+
duration_ms: number;
|
|
237
|
+
success: boolean;
|
|
238
|
+
retries: number;
|
|
239
|
+
verdict?: string;
|
|
240
|
+
plan_name?: string;
|
|
241
|
+
}): void {
|
|
242
|
+
recordMetric({ type: "gemini_call", ...data });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function recordDiscoveryCompleted(data: {
|
|
246
|
+
specialist: string;
|
|
247
|
+
approach_count: number;
|
|
248
|
+
variant_count: number;
|
|
249
|
+
question_count: number;
|
|
250
|
+
plan_name?: string;
|
|
251
|
+
}): void {
|
|
252
|
+
recordMetric({ type: "discovery_completed", ...data });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function recordDocumentationExtracted(data: {
|
|
256
|
+
prompt_num: number;
|
|
257
|
+
variant?: string | null;
|
|
258
|
+
files_affected: number;
|
|
259
|
+
plan_name?: string;
|
|
260
|
+
}): void {
|
|
261
|
+
recordMetric({ type: "documentation_extracted", ...data });
|
|
262
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan directory path utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { mkdirSync, existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { getPlanDir } from "./git.js";
|
|
8
|
+
import { logInfo } from "./observability.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Plan directory structure subdirectories.
|
|
12
|
+
*/
|
|
13
|
+
const PLAN_SUBDIRS = [
|
|
14
|
+
"prompts",
|
|
15
|
+
"findings",
|
|
16
|
+
"findings/_archive",
|
|
17
|
+
"design",
|
|
18
|
+
"user_feedback",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ensure the plan directory exists with all required subdirectories.
|
|
23
|
+
* Creates on demand if not present.
|
|
24
|
+
*
|
|
25
|
+
* @returns The plan directory path
|
|
26
|
+
*/
|
|
27
|
+
export function ensurePlanDir(): string {
|
|
28
|
+
const planDir = getPlanDir();
|
|
29
|
+
|
|
30
|
+
if (!existsSync(planDir)) {
|
|
31
|
+
logInfo("plans.create_dir", { path: planDir });
|
|
32
|
+
mkdirSync(planDir, { recursive: true });
|
|
33
|
+
|
|
34
|
+
// Create subdirectories
|
|
35
|
+
for (const subdir of PLAN_SUBDIRS) {
|
|
36
|
+
const subdirPath = join(planDir, subdir);
|
|
37
|
+
mkdirSync(subdirPath, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return planDir;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get paths for all standard plan files.
|
|
46
|
+
*/
|
|
47
|
+
export function getPlanPaths(): {
|
|
48
|
+
planDir: string;
|
|
49
|
+
plan: string;
|
|
50
|
+
userInput: string;
|
|
51
|
+
curator: string;
|
|
52
|
+
summary: string;
|
|
53
|
+
prompts: string;
|
|
54
|
+
findings: string;
|
|
55
|
+
design: string;
|
|
56
|
+
userFeedback: string;
|
|
57
|
+
} {
|
|
58
|
+
const planDir = getPlanDir();
|
|
59
|
+
return {
|
|
60
|
+
planDir,
|
|
61
|
+
plan: join(planDir, "plan.md"),
|
|
62
|
+
userInput: join(planDir, "user_input.md"),
|
|
63
|
+
curator: join(planDir, "curator.md"),
|
|
64
|
+
summary: join(planDir, "summary.md"),
|
|
65
|
+
prompts: join(planDir, "prompts"),
|
|
66
|
+
findings: join(planDir, "findings"),
|
|
67
|
+
design: join(planDir, "design"),
|
|
68
|
+
userFeedback: join(planDir, "user_feedback"),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the prompt file path for a given prompt number and optional variant.
|
|
74
|
+
*/
|
|
75
|
+
export function getPromptPath(number: number, variant?: string | null): string {
|
|
76
|
+
const paths = getPlanPaths();
|
|
77
|
+
const filename = variant ? `${number}_${variant}.md` : `${number}.md`;
|
|
78
|
+
return join(paths.prompts, filename);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the findings file path for a given specialist.
|
|
83
|
+
*/
|
|
84
|
+
export function getFindingsPath(specialist: string): string {
|
|
85
|
+
const paths = getPlanPaths();
|
|
86
|
+
return join(paths.findings, `${specialist}.yaml`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get the user feedback file path (ephemeral, created by block commands).
|
|
91
|
+
*/
|
|
92
|
+
export function getUserFeedbackPath(id: string, ext: string = ".yaml"): string {
|
|
93
|
+
const paths = getPlanPaths();
|
|
94
|
+
return join(paths.userFeedback, `${id}${ext}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a plan directory exists for the current branch.
|
|
99
|
+
*/
|
|
100
|
+
export function planExists(): boolean {
|
|
101
|
+
return existsSync(getPlanDir());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get prompt identifier string (e.g., "1", "2_A", "3_B").
|
|
106
|
+
*/
|
|
107
|
+
export function getPromptId(number: number, variant: string | null): string {
|
|
108
|
+
return variant ? `${number}_${variant}` : `${number}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parse prompt identifier string into number and variant.
|
|
113
|
+
*/
|
|
114
|
+
export function parsePromptId(id: string): { number: number; variant: string | null } {
|
|
115
|
+
const match = id.match(/^(\d+)(?:_([A-Z]))?$/);
|
|
116
|
+
if (!match) {
|
|
117
|
+
throw new Error(`Invalid prompt identifier: ${id}`);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
number: parseInt(match[1], 10),
|
|
121
|
+
variant: match[2] || null,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get approach ID string: "{number}" or "{number}_{variant}"
|
|
127
|
+
*/
|
|
128
|
+
export function getApproachId(number: number, variant: string | null): string {
|
|
129
|
+
return variant ? `${number}_${variant}` : `${number}`;
|
|
130
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan file I/O operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, appendFileSync } from "fs";
|
|
6
|
+
import { getBranch } from "./git.js";
|
|
7
|
+
import { ensurePlanDir, getPlanPaths } from "./paths.js";
|
|
8
|
+
import { readMarkdownFile, writeMarkdownWithFrontMatter } from "./markdown.js";
|
|
9
|
+
|
|
10
|
+
export interface PlanFrontMatter {
|
|
11
|
+
stage: "draft" | "in_progress" | "completed";
|
|
12
|
+
branch_name: string;
|
|
13
|
+
audits: Array<{
|
|
14
|
+
review_context: string;
|
|
15
|
+
decision: "approved" | "needs_clarification" | "rejected";
|
|
16
|
+
total_questions: number;
|
|
17
|
+
were_changes_suggested: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
reviews: Array<{
|
|
20
|
+
review_context: string;
|
|
21
|
+
decision: "approved" | "needs_changes" | "rejected";
|
|
22
|
+
total_questions: number;
|
|
23
|
+
were_changes_suggested: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read plan.md and return parsed front matter + content.
|
|
29
|
+
*/
|
|
30
|
+
export function readPlan(): { frontMatter: PlanFrontMatter; content: string } | null {
|
|
31
|
+
const paths = getPlanPaths();
|
|
32
|
+
const result = readMarkdownFile(paths.plan);
|
|
33
|
+
if (!result) return null;
|
|
34
|
+
return {
|
|
35
|
+
frontMatter: result.frontMatter as unknown as PlanFrontMatter,
|
|
36
|
+
content: result.content,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Write plan.md with front matter and content.
|
|
42
|
+
*/
|
|
43
|
+
export function writePlan(frontMatter: Partial<PlanFrontMatter>, content: string): void {
|
|
44
|
+
const paths = getPlanPaths();
|
|
45
|
+
ensurePlanDir();
|
|
46
|
+
|
|
47
|
+
const defaults: PlanFrontMatter = {
|
|
48
|
+
stage: "draft",
|
|
49
|
+
branch_name: getBranch(),
|
|
50
|
+
audits: [],
|
|
51
|
+
reviews: [],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const merged = { ...defaults, ...frontMatter };
|
|
55
|
+
writeMarkdownWithFrontMatter(paths.plan, merged, content);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Update plan stage without modifying content.
|
|
60
|
+
*/
|
|
61
|
+
export function updatePlanStage(stage: PlanFrontMatter["stage"]): void {
|
|
62
|
+
const plan = readPlan();
|
|
63
|
+
if (!plan) return;
|
|
64
|
+
|
|
65
|
+
const updatedFrontMatter = { ...plan.frontMatter, stage };
|
|
66
|
+
const paths = getPlanPaths();
|
|
67
|
+
writeMarkdownWithFrontMatter(paths.plan, updatedFrontMatter, plan.content);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read user_input.md content.
|
|
72
|
+
*/
|
|
73
|
+
export function readUserInput(): string | null {
|
|
74
|
+
const paths = getPlanPaths();
|
|
75
|
+
if (!existsSync(paths.userInput)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return readFileSync(paths.userInput, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Append content to user_input.md.
|
|
83
|
+
*/
|
|
84
|
+
export function appendUserInput(content: string): void {
|
|
85
|
+
const paths = getPlanPaths();
|
|
86
|
+
ensurePlanDir();
|
|
87
|
+
|
|
88
|
+
// Initialize file if it doesn't exist
|
|
89
|
+
if (!existsSync(paths.userInput)) {
|
|
90
|
+
writeFileSync(paths.userInput, "# User Input Log\n\n", "utf-8");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Append with timestamp
|
|
94
|
+
const timestamp = new Date().toISOString();
|
|
95
|
+
const entry = `\n---\n**${timestamp}**\n\n${content}\n`;
|
|
96
|
+
appendFileSync(paths.userInput, entry, "utf-8");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Read summary.md content.
|
|
101
|
+
*/
|
|
102
|
+
export function readSummary(): string | null {
|
|
103
|
+
const paths = getPlanPaths();
|
|
104
|
+
if (!existsSync(paths.summary)) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return readFileSync(paths.summary, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Write summary.md content.
|
|
112
|
+
*/
|
|
113
|
+
export function writeSummary(content: string): void {
|
|
114
|
+
const paths = getPlanPaths();
|
|
115
|
+
ensurePlanDir();
|
|
116
|
+
writeFileSync(paths.summary, content, "utf-8");
|
|
117
|
+
}
|