rhachet-roles-bhrain 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.test/getContextOpenAI.js +1 -1
- package/dist/.test/getContextOpenAI.js.map +1 -1
- package/dist/domain.operations/review/compileReviewPrompt.d.ts +22 -0
- package/dist/domain.operations/review/compileReviewPrompt.js +95 -0
- package/dist/domain.operations/review/compileReviewPrompt.js.map +1 -0
- package/dist/domain.operations/review/enumFilesFromDiffs.d.ts +8 -0
- package/dist/domain.operations/review/enumFilesFromDiffs.js +74 -0
- package/dist/domain.operations/review/enumFilesFromDiffs.js.map +1 -0
- package/dist/domain.operations/review/enumFilesFromGlob.d.ts +8 -0
- package/dist/domain.operations/review/enumFilesFromGlob.js +31 -0
- package/dist/domain.operations/review/enumFilesFromGlob.js.map +1 -0
- package/dist/domain.operations/review/estimateTokenCount.d.ts +9 -0
- package/dist/domain.operations/review/estimateTokenCount.js +20 -0
- package/dist/domain.operations/review/estimateTokenCount.js.map +1 -0
- package/dist/domain.operations/review/formatReviewOutput.d.ts +14 -0
- package/dist/domain.operations/review/formatReviewOutput.js +42 -0
- package/dist/domain.operations/review/formatReviewOutput.js.map +1 -0
- package/dist/domain.operations/review/genTokenBreakdownMarkdown.d.ts +19 -0
- package/dist/domain.operations/review/genTokenBreakdownMarkdown.js +110 -0
- package/dist/domain.operations/review/genTokenBreakdownMarkdown.js.map +1 -0
- package/dist/domain.operations/review/genTokenBreakdownReport.d.ts +24 -0
- package/dist/domain.operations/review/genTokenBreakdownReport.js +64 -0
- package/dist/domain.operations/review/genTokenBreakdownReport.js.map +1 -0
- package/dist/domain.operations/review/invokeClaudeCode.d.ts +22 -0
- package/dist/domain.operations/review/invokeClaudeCode.js +92 -0
- package/dist/domain.operations/review/invokeClaudeCode.js.map +1 -0
- package/dist/domain.operations/review/writeInputArtifacts.d.ts +27 -0
- package/dist/domain.operations/review/writeInputArtifacts.js +50 -0
- package/dist/domain.operations/review/writeInputArtifacts.js.map +1 -0
- package/dist/domain.operations/review/writeOutputArtifacts.d.ts +12 -0
- package/dist/domain.operations/review/writeOutputArtifacts.js +46 -0
- package/dist/domain.operations/review/writeOutputArtifacts.js.map +1 -0
- package/dist/roles/getRoleRegistry.js +2 -1
- package/dist/roles/getRoleRegistry.js.map +1 -1
- package/dist/roles/getRoleRegistry.readme.js +6 -0
- package/dist/roles/getRoleRegistry.readme.js.map +1 -1
- package/dist/roles/reviewer/briefs/review.tactics.md +60 -0
- package/dist/roles/reviewer/getReviewerRole.d.ts +6 -0
- package/dist/roles/reviewer/getReviewerRole.js +80 -0
- package/dist/roles/reviewer/getReviewerRole.js.map +1 -0
- package/dist/roles/reviewer/skills/review/review.d.ts +57 -0
- package/dist/roles/reviewer/skills/review/review.js +445 -0
- package/dist/roles/reviewer/skills/review/review.js.map +1 -0
- package/dist/roles/reviewer/skills/review/review.sh +21 -0
- package/dist/roles/reviewer/skills/review/review.ts +575 -0
- package/dist/roles/thinker/getThinkerRole.js +1 -1
- package/dist/roles/thinker/getThinkerRole.js.map +1 -1
- package/dist/roles/thinker/skills/brief.articulate/.demo/article.vision.v2025_08_19..i1.via_chatgpt.md +47 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/article.vision.v2025_08_19.i2.via_rhachet.md +60 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/diverge.v2025_08_17.i1.md +62 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/diverge.v2025_08_17.i1.with_feedback.md +89 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/diverge.v2025_08_17.i2.md +47 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/joke.v2025_08_15.i1.md +44 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/joke.v2025_08_15.i2.md +63 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/joke.v2025_08_15.i3.md +51 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/user-journey.v2025_08_17.i1.md +62 -0
- package/dist/roles/thinker/skills/brief.articulate/.demo/user-journey.v2025_08_17.i2.md +49 -0
- package/dist/roles/thinker/skills/brief.articulate/.readme.md +0 -0
- package/dist/roles/thinker/skills/brief.articulate/stepArticulate.skill.js +1 -1
- package/dist/roles/thinker/skills/brief.articulate/stepArticulate.skill.js.map +1 -1
- package/dist/roles/thinker/skills/brief.articulate/stepArticulate.skill.ts +168 -0
- package/dist/roles/thinker/skills/brief.articulate/stepArticulate.ts +157 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/joke.types.v2025_08_28.i1.md +93 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/joke.types.v2025_08_28.i2.md +84 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/joke.types.v2025_09_28.i1.no_focus_context.md +8 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/joke.types.v2025_09_28.i2.md +54 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/persona.usecases.v2025_08_28.i1.md +62 -0
- package/dist/roles/thinker/skills/brief.catalogize/.demo/persona.usecases.v2025_08_28.i2.md +64 -0
- package/dist/roles/thinker/skills/brief.catalogize/.readme.md +5 -0
- package/dist/roles/thinker/skills/brief.catalogize/stepCatalogize.skill.js +1 -1
- package/dist/roles/thinker/skills/brief.catalogize/stepCatalogize.skill.js.map +1 -1
- package/dist/roles/thinker/skills/brief.catalogize/stepCatalogize.skill.ts +173 -0
- package/dist/roles/thinker/skills/brief.catalogize/stepCatalogize.ts +132 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.input.example.i4.md +3 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.input.example.i5.md +3 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.input.example.i6.md +3 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.input.example.md +3 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i1.md +52 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i2.md +51 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i3.md +47 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i4.md +62 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i5.md +47 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.demo/user.journey.roadtrip.v2025_08_27.i6.md +53 -0
- package/dist/roles/thinker/skills/brief.demonstrate/.readme +3 -0
- package/dist/roles/thinker/skills/brief.demonstrate/stepDemonstrate.skill.js +1 -1
- package/dist/roles/thinker/skills/brief.demonstrate/stepDemonstrate.skill.js.map +1 -1
- package/dist/roles/thinker/skills/brief.demonstrate/stepDemonstrate.skill.ts +190 -0
- package/dist/roles/thinker/skills/brief.demonstrate/stepDemonstrate.ts +164 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input1.cluster.v2025_08_17.i1.md +72 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input1.cluster.v2025_08_17.i2.md +53 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input1.cluster.v2025_08_17.i3.which_objectives.md +58 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input1.cluster.v2025_08_17.i5.which_personas.md +64 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input2.cluster.v2025_08_17.i1.md +67 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input2.cluster.v2025_08_17.i2.md +49 -0
- package/dist/roles/thinker/skills/khue.cluster/.demo/user.journeys.input2.cluster.v2025_08_17.i3.md +59 -0
- package/dist/roles/thinker/skills/khue.cluster/.readme.md +0 -0
- package/dist/roles/thinker/skills/khue.cluster/stepCluster.skill.js +1 -1
- package/dist/roles/thinker/skills/khue.cluster/stepCluster.skill.js.map +1 -1
- package/dist/roles/thinker/skills/khue.cluster/stepCluster.skill.ts +174 -0
- package/dist/roles/thinker/skills/khue.cluster/stepCluster.ts +150 -0
- package/dist/roles/thinker/skills/khue.decompose/.readme.md +9 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/joke.examples.v2025_08_17.i2.md +23 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/joke.examples.v2025_08_17.i3.md +23 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/joke.varieties.v2025_08_17.i1.md +23 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/userjourney.examples.v2025_08_17.i1.md +9 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/userjourney.examples.v2025_08_17.i2.md +9 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/userjourney.examples.v2025_08_17.i3.md +23 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/userjourney.examples.v2025_08_17.i4.folksy.md +9 -0
- package/dist/roles/thinker/skills/khue.diverge/.demo/userjourney.examples.v2025_08_17.i5.folksy.md +23 -0
- package/dist/roles/thinker/skills/khue.diverge/.readme.md +0 -0
- package/dist/roles/thinker/skills/khue.diverge/stepDiverge.skill.js +1 -1
- package/dist/roles/thinker/skills/khue.diverge/stepDiverge.skill.js.map +1 -1
- package/dist/roles/thinker/skills/khue.diverge/stepDiverge.skill.ts +149 -0
- package/dist/roles/thinker/skills/khue.diverge/stepDiverge.ts +151 -0
- package/dist/roles/thinker/skills/khue.encompose/.readme.md +7 -0
- package/dist/roles/thinker/skills/khue.instantiate/.readme.md +14 -0
- package/dist/roles/thinker/skills/khue.instantiate/stepInstantiate.skill.js +1 -1
- package/dist/roles/thinker/skills/khue.instantiate/stepInstantiate.skill.js.map +1 -1
- package/dist/roles/thinker/skills/khue.instantiate/stepInstantiate.skill.ts +190 -0
- package/dist/roles/thinker/skills/khue.instantiate/stepInstantiate.ts +132 -0
- package/dist/roles/thinker/skills/khue.triage/.demo/laughs.v2025_08_18.i1.md +29 -0
- package/dist/roles/thinker/skills/khue.triage/.demo/user.journeys.v2025_08_17.i1.md +86 -0
- package/dist/roles/thinker/skills/khue.triage/.demo/user.journeys.v2025_08_17.i2.md +68 -0
- package/dist/roles/thinker/skills/khue.triage/.readme.md +0 -0
- package/dist/roles/thinker/skills/khue.triage/stepTriage.skill.js +1 -1
- package/dist/roles/thinker/skills/khue.triage/stepTriage.skill.js.map +1 -1
- package/dist/roles/thinker/skills/khue.triage/stepTriage.skill.ts +174 -0
- package/dist/roles/thinker/skills/khue.triage/stepTriage.ts +153 -0
- package/package.json +7 -6
- package/readme.md +55 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import { BadRequestError } from 'helpful-errors';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
import { compileReviewPrompt } from '@src/domain.operations/review/compileReviewPrompt';
|
|
6
|
+
import { enumFilesFromDiffs } from '@src/domain.operations/review/enumFilesFromDiffs';
|
|
7
|
+
import { enumFilesFromGlob } from '@src/domain.operations/review/enumFilesFromGlob';
|
|
8
|
+
import { formatReviewOutput } from '@src/domain.operations/review/formatReviewOutput';
|
|
9
|
+
import { genTokenBreakdownMarkdown } from '@src/domain.operations/review/genTokenBreakdownMarkdown';
|
|
10
|
+
import { genTokenBreakdownReport } from '@src/domain.operations/review/genTokenBreakdownReport';
|
|
11
|
+
import { invokeClaudeCode } from '@src/domain.operations/review/invokeClaudeCode';
|
|
12
|
+
import { writeInputArtifacts } from '@src/domain.operations/review/writeInputArtifacts';
|
|
13
|
+
import { writeOutputArtifacts } from '@src/domain.operations/review/writeOutputArtifacts';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* .what = result of stepReview execution
|
|
17
|
+
* .why = enables caller to inspect review outcome and artifacts
|
|
18
|
+
*/
|
|
19
|
+
export type StepReviewResult = {
|
|
20
|
+
review: {
|
|
21
|
+
formatted: string;
|
|
22
|
+
};
|
|
23
|
+
log: {
|
|
24
|
+
dir: string;
|
|
25
|
+
};
|
|
26
|
+
output: {
|
|
27
|
+
path: string;
|
|
28
|
+
};
|
|
29
|
+
metrics: {
|
|
30
|
+
files: {
|
|
31
|
+
rulesCount: number;
|
|
32
|
+
targetsCount: number;
|
|
33
|
+
};
|
|
34
|
+
expected: {
|
|
35
|
+
tokens: {
|
|
36
|
+
estimate: number;
|
|
37
|
+
contextWindowPercent: number;
|
|
38
|
+
};
|
|
39
|
+
cost: {
|
|
40
|
+
estimate: number;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
realized: {
|
|
44
|
+
tokens: {
|
|
45
|
+
input: number;
|
|
46
|
+
inputCacheCreation: number;
|
|
47
|
+
inputCacheRead: number;
|
|
48
|
+
output: number;
|
|
49
|
+
};
|
|
50
|
+
cost: {
|
|
51
|
+
input: number;
|
|
52
|
+
cacheWrite: number;
|
|
53
|
+
cacheRead: number;
|
|
54
|
+
output: number;
|
|
55
|
+
total: number;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* .what = generates ISO timestamp for log directory naming
|
|
63
|
+
* .why = enables unique, sortable log directories
|
|
64
|
+
*/
|
|
65
|
+
const genLogTimestamp = (): string => {
|
|
66
|
+
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* .what = simple spinner for CLI feedback
|
|
71
|
+
* .why = shows progress during long-running operations
|
|
72
|
+
*/
|
|
73
|
+
const withSpinner = async <T>(input: {
|
|
74
|
+
message: string;
|
|
75
|
+
operation: () => Promise<T>;
|
|
76
|
+
}): Promise<T> => {
|
|
77
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
let i = 0;
|
|
80
|
+
|
|
81
|
+
// print title once
|
|
82
|
+
console.log(input.message);
|
|
83
|
+
|
|
84
|
+
// render only the elapsed time branch line
|
|
85
|
+
const render = (frame: string) => {
|
|
86
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
87
|
+
process.stdout.write(`\r └─ elapsed: ${elapsed}s ${frame} `);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
render(frames[0]!);
|
|
91
|
+
const interval = setInterval(() => {
|
|
92
|
+
i = (i + 1) % frames.length;
|
|
93
|
+
render(frames[i]!);
|
|
94
|
+
}, 100);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const result = await input.operation();
|
|
98
|
+
clearInterval(interval);
|
|
99
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
100
|
+
process.stdout.write(`\r └─ elapsed: ${elapsed}s ✓\n\n`);
|
|
101
|
+
return result;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
clearInterval(interval);
|
|
104
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
105
|
+
process.stdout.write(`\r └─ elapsed: ${elapsed}s ✗\n`);
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* .what = executes a code review against specified rules and targets
|
|
112
|
+
* .why = core orchestration flow for reviewer role
|
|
113
|
+
*/
|
|
114
|
+
export const stepReview = async (input: {
|
|
115
|
+
rules: string | string[];
|
|
116
|
+
diffs?: string;
|
|
117
|
+
paths?: string | string[];
|
|
118
|
+
output: string;
|
|
119
|
+
mode: 'soft' | 'hard';
|
|
120
|
+
cwd?: string;
|
|
121
|
+
}): Promise<StepReviewResult> => {
|
|
122
|
+
const cwd = input.cwd ?? process.cwd();
|
|
123
|
+
|
|
124
|
+
// validate that at least one of rules, diffs, or paths is specified
|
|
125
|
+
const hasRules =
|
|
126
|
+
input.rules && (Array.isArray(input.rules) ? input.rules.length > 0 : true);
|
|
127
|
+
const hasDiffs = !!input.diffs;
|
|
128
|
+
const hasPaths =
|
|
129
|
+
input.paths && (Array.isArray(input.paths) ? input.paths.length > 0 : true);
|
|
130
|
+
if (!hasRules && !hasDiffs && !hasPaths)
|
|
131
|
+
throw new BadRequestError(
|
|
132
|
+
'must specify at least one of --rules, --diffs, or --paths',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// validate output parent directory exists
|
|
136
|
+
const outputParent = path.dirname(input.output);
|
|
137
|
+
const outputParentAbsolute = path.isAbsolute(outputParent)
|
|
138
|
+
? outputParent
|
|
139
|
+
: path.join(cwd, outputParent);
|
|
140
|
+
try {
|
|
141
|
+
await fs.access(outputParentAbsolute);
|
|
142
|
+
} catch {
|
|
143
|
+
throw new BadRequestError('output path parent directory does not exist', {
|
|
144
|
+
outputParent: outputParentAbsolute,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// enumerate rule files
|
|
149
|
+
const ruleGlobs = Array.isArray(input.rules)
|
|
150
|
+
? input.rules
|
|
151
|
+
: [input.rules].filter(Boolean);
|
|
152
|
+
const ruleFiles = await enumFilesFromGlob({ glob: ruleGlobs, cwd });
|
|
153
|
+
if (ruleGlobs.length > 0 && ruleFiles.length === 0)
|
|
154
|
+
throw new BadRequestError(
|
|
155
|
+
'--rules glob was ineffective; matched zero files',
|
|
156
|
+
{
|
|
157
|
+
rules: input.rules,
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// enumerate target files from diffs
|
|
162
|
+
const targetFilesFromDiffs = input.diffs
|
|
163
|
+
? await enumFilesFromDiffs({
|
|
164
|
+
range: input.diffs as 'uptil-main' | 'uptil-commit' | 'uptil-staged',
|
|
165
|
+
cwd,
|
|
166
|
+
})
|
|
167
|
+
: [];
|
|
168
|
+
|
|
169
|
+
// enumerate target files from paths
|
|
170
|
+
const pathGlobs = input.paths
|
|
171
|
+
? Array.isArray(input.paths)
|
|
172
|
+
? input.paths
|
|
173
|
+
: [input.paths]
|
|
174
|
+
: [];
|
|
175
|
+
const positivePathGlobs = pathGlobs.filter((p) => !p.startsWith('!'));
|
|
176
|
+
const negativePathGlobs = pathGlobs
|
|
177
|
+
.filter((p) => p.startsWith('!'))
|
|
178
|
+
.map((p) => p.slice(1));
|
|
179
|
+
const targetFilesFromPaths = await enumFilesFromGlob({
|
|
180
|
+
glob: positivePathGlobs,
|
|
181
|
+
cwd,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// union target files from diffs and paths, then apply global exclusions
|
|
185
|
+
const targetFilesUnion = [
|
|
186
|
+
...new Set([...targetFilesFromDiffs, ...targetFilesFromPaths]),
|
|
187
|
+
];
|
|
188
|
+
const targetFiles = targetFilesUnion
|
|
189
|
+
.filter((file) => {
|
|
190
|
+
for (const pattern of negativePathGlobs) {
|
|
191
|
+
if (file === pattern || file.endsWith(`/${pattern}`)) return false;
|
|
192
|
+
if (pattern.includes('*')) {
|
|
193
|
+
const regex = new RegExp(
|
|
194
|
+
'^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$',
|
|
195
|
+
);
|
|
196
|
+
if (regex.test(file)) return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
})
|
|
201
|
+
.sort();
|
|
202
|
+
|
|
203
|
+
// validate combined scope is non-empty
|
|
204
|
+
if (targetFiles.length === 0)
|
|
205
|
+
throw new BadRequestError('combined scope resolves to zero files', {
|
|
206
|
+
diffs: input.diffs,
|
|
207
|
+
paths: input.paths,
|
|
208
|
+
diffsMatched: targetFilesFromDiffs.length,
|
|
209
|
+
pathsMatched: targetFilesFromPaths.length,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// create log directory early for debugging
|
|
213
|
+
const logDir = path.join(cwd, '.log', 'bhrain', 'review', genLogTimestamp());
|
|
214
|
+
await fs.mkdir(logDir, { recursive: true });
|
|
215
|
+
|
|
216
|
+
// write scope immediately for debugging
|
|
217
|
+
await fs.writeFile(
|
|
218
|
+
path.join(logDir, 'input.scope.json'),
|
|
219
|
+
JSON.stringify({ ruleFiles, targetFiles }, null, 2),
|
|
220
|
+
'utf-8',
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// read file contents for prompt compilation
|
|
224
|
+
const readFileContent = async (file: string) => {
|
|
225
|
+
try {
|
|
226
|
+
return await fs.readFile(path.join(cwd, file), 'utf-8');
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (!(error instanceof Error)) throw error;
|
|
229
|
+
throw new BadRequestError(`failed to read file: ${file}`, {
|
|
230
|
+
file,
|
|
231
|
+
fullPath: path.join(cwd, file),
|
|
232
|
+
error: error.message,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const ruleContents = await Promise.all(
|
|
237
|
+
ruleFiles.map(async (file) => ({
|
|
238
|
+
path: file,
|
|
239
|
+
content: await readFileContent(file),
|
|
240
|
+
})),
|
|
241
|
+
);
|
|
242
|
+
const targetContents = await Promise.all(
|
|
243
|
+
targetFiles.map(async (file) => ({
|
|
244
|
+
path: file,
|
|
245
|
+
content: await readFileContent(file),
|
|
246
|
+
})),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// generate and write token breakdown reports
|
|
250
|
+
const allContents = [...ruleContents, ...targetContents];
|
|
251
|
+
const allBreakdown = genTokenBreakdownReport({ files: allContents });
|
|
252
|
+
const rulesBreakdown = genTokenBreakdownReport({ files: ruleContents });
|
|
253
|
+
const targetsBreakdown = genTokenBreakdownReport({ files: targetContents });
|
|
254
|
+
await fs.writeFile(
|
|
255
|
+
path.join(logDir, 'tokens.expected.json'),
|
|
256
|
+
JSON.stringify(
|
|
257
|
+
{
|
|
258
|
+
all: allBreakdown,
|
|
259
|
+
rules: rulesBreakdown,
|
|
260
|
+
targets: targetsBreakdown,
|
|
261
|
+
},
|
|
262
|
+
null,
|
|
263
|
+
2,
|
|
264
|
+
),
|
|
265
|
+
'utf-8',
|
|
266
|
+
);
|
|
267
|
+
await fs.writeFile(
|
|
268
|
+
path.join(logDir, 'tokens.expected.md'),
|
|
269
|
+
genTokenBreakdownMarkdown({
|
|
270
|
+
all: allBreakdown,
|
|
271
|
+
rules: rulesBreakdown,
|
|
272
|
+
targets: targetsBreakdown,
|
|
273
|
+
}),
|
|
274
|
+
'utf-8',
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// compile review prompt
|
|
278
|
+
const promptResult = compileReviewPrompt({
|
|
279
|
+
rules: ruleContents,
|
|
280
|
+
targets: targetContents,
|
|
281
|
+
mode: input.mode,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// write metrics.expected immediately after files are read
|
|
285
|
+
await fs.writeFile(
|
|
286
|
+
path.join(logDir, 'metrics.expected.json'),
|
|
287
|
+
JSON.stringify(
|
|
288
|
+
{
|
|
289
|
+
files: {
|
|
290
|
+
rulesCount: ruleFiles.length,
|
|
291
|
+
targetsCount: targetFiles.length,
|
|
292
|
+
},
|
|
293
|
+
tokens: {
|
|
294
|
+
estimate: promptResult.tokenEstimate,
|
|
295
|
+
contextWindowPercent: promptResult.contextWindowPercent,
|
|
296
|
+
},
|
|
297
|
+
cost: {
|
|
298
|
+
estimate: promptResult.costEstimate,
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
null,
|
|
302
|
+
2,
|
|
303
|
+
),
|
|
304
|
+
'utf-8',
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// write input artifacts
|
|
308
|
+
await writeInputArtifacts({
|
|
309
|
+
logDir,
|
|
310
|
+
args: {
|
|
311
|
+
rules: input.rules,
|
|
312
|
+
diffs: input.diffs,
|
|
313
|
+
paths: input.paths,
|
|
314
|
+
output: input.output,
|
|
315
|
+
mode: input.mode,
|
|
316
|
+
},
|
|
317
|
+
scope: {
|
|
318
|
+
ruleFiles,
|
|
319
|
+
targetFiles,
|
|
320
|
+
},
|
|
321
|
+
metrics: {
|
|
322
|
+
tokenEstimate: promptResult.tokenEstimate,
|
|
323
|
+
contextWindowPercent: promptResult.contextWindowPercent,
|
|
324
|
+
costEstimate: promptResult.costEstimate,
|
|
325
|
+
},
|
|
326
|
+
prompt: promptResult.prompt,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// emit metrics.expected before invocation
|
|
330
|
+
const logDirRelative = path.relative(cwd, logDir);
|
|
331
|
+
console.log(
|
|
332
|
+
`
|
|
333
|
+
🔭 metrics.expected
|
|
334
|
+
├─ files
|
|
335
|
+
│ ├─ rules: ${ruleFiles.length}
|
|
336
|
+
│ └─ targets: ${targetFiles.length}
|
|
337
|
+
├─ tokens
|
|
338
|
+
│ ├─ estimate: ${promptResult.tokenEstimate}
|
|
339
|
+
│ └─ context: ${promptResult.contextWindowPercent.toFixed(1)}%
|
|
340
|
+
└─ cost
|
|
341
|
+
└─ estimate: $${promptResult.costEstimate.toFixed(4)}
|
|
342
|
+
|
|
343
|
+
🪵 logs
|
|
344
|
+
├─ scope: ${logDirRelative}/input.scope.json
|
|
345
|
+
├─ metrics: ${logDirRelative}/metrics.expected.json
|
|
346
|
+
└─ tokens: ${logDirRelative}/tokens.expected.md
|
|
347
|
+
`.trim(),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// invoke claude-code with spinner
|
|
351
|
+
console.log('');
|
|
352
|
+
const brainResult = await withSpinner({
|
|
353
|
+
message: "🐢 let's review!",
|
|
354
|
+
operation: () => invokeClaudeCode({ prompt: promptResult.prompt, cwd }),
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// calculate realized costs per token type
|
|
358
|
+
const realizedCosts = (() => {
|
|
359
|
+
const input = (brainResult.usage.inputTokens / 1_000_000) * 3;
|
|
360
|
+
const cacheWrite =
|
|
361
|
+
(brainResult.usage.inputTokensCacheCreation / 1_000_000) * 3.75;
|
|
362
|
+
const cacheRead =
|
|
363
|
+
(brainResult.usage.inputTokensCacheRead / 1_000_000) * 0.3;
|
|
364
|
+
const output = (brainResult.usage.outputTokens / 1_000_000) * 15;
|
|
365
|
+
const total =
|
|
366
|
+
Math.round((input + cacheWrite + cacheRead + output) * 10000) / 10000;
|
|
367
|
+
return { input, cacheWrite, cacheRead, output, total };
|
|
368
|
+
})();
|
|
369
|
+
|
|
370
|
+
// write metrics.realized after invocation
|
|
371
|
+
await fs.writeFile(
|
|
372
|
+
path.join(logDir, 'metrics.realized.json'),
|
|
373
|
+
JSON.stringify(
|
|
374
|
+
{
|
|
375
|
+
tokens: {
|
|
376
|
+
input: brainResult.usage.inputTokens,
|
|
377
|
+
inputCacheCreation: brainResult.usage.inputTokensCacheCreation,
|
|
378
|
+
inputCacheRead: brainResult.usage.inputTokensCacheRead,
|
|
379
|
+
output: brainResult.usage.outputTokens,
|
|
380
|
+
},
|
|
381
|
+
cost: {
|
|
382
|
+
input: realizedCosts.input,
|
|
383
|
+
cacheWrite: realizedCosts.cacheWrite,
|
|
384
|
+
cacheRead: realizedCosts.cacheRead,
|
|
385
|
+
output: realizedCosts.output,
|
|
386
|
+
total: realizedCosts.total,
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
null,
|
|
390
|
+
2,
|
|
391
|
+
),
|
|
392
|
+
'utf-8',
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
// parse issues from review text
|
|
396
|
+
const reviewIssues = (() => {
|
|
397
|
+
// extract JSON from the review text (may be wrapped in markdown code blocks)
|
|
398
|
+
const jsonMatch = brainResult.review.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
399
|
+
const jsonText = jsonMatch?.[1]?.trim() ?? brainResult.review.trim();
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
return JSON.parse(jsonText) as {
|
|
403
|
+
issues: Array<{
|
|
404
|
+
type: 'blocker' | 'nitpick';
|
|
405
|
+
message: string;
|
|
406
|
+
file?: string;
|
|
407
|
+
line?: number;
|
|
408
|
+
}>;
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
throw new BadRequestError(
|
|
412
|
+
'failed to parse review issues from claude response',
|
|
413
|
+
{
|
|
414
|
+
review: brainResult.review,
|
|
415
|
+
jsonText,
|
|
416
|
+
error: error instanceof Error ? error.message : String(error),
|
|
417
|
+
},
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
})();
|
|
421
|
+
|
|
422
|
+
// format review output
|
|
423
|
+
const formattedReview = formatReviewOutput({
|
|
424
|
+
response: reviewIssues,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// write output artifacts
|
|
428
|
+
await writeOutputArtifacts({
|
|
429
|
+
logDir,
|
|
430
|
+
response: brainResult.response,
|
|
431
|
+
review: formattedReview,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// write final review to output path
|
|
435
|
+
const outputAbsolute = path.isAbsolute(input.output)
|
|
436
|
+
? input.output
|
|
437
|
+
: path.join(cwd, input.output);
|
|
438
|
+
await fs.writeFile(outputAbsolute, formattedReview, 'utf-8');
|
|
439
|
+
|
|
440
|
+
// emit metrics.realized after invocation
|
|
441
|
+
console.log(
|
|
442
|
+
`
|
|
443
|
+
✨ metrics.realized
|
|
444
|
+
├─ tokens
|
|
445
|
+
│ ├─ input: ${brainResult.usage.inputTokens}
|
|
446
|
+
│ ├─ cache.write: ${brainResult.usage.inputTokensCacheCreation}
|
|
447
|
+
│ ├─ cache.read: ${brainResult.usage.inputTokensCacheRead}
|
|
448
|
+
│ └─ output: ${brainResult.usage.outputTokens}
|
|
449
|
+
└─ cost
|
|
450
|
+
├─ input: $${realizedCosts.input.toFixed(4)}
|
|
451
|
+
├─ cache.write: $${realizedCosts.cacheWrite.toFixed(4)}
|
|
452
|
+
├─ cache.read: $${realizedCosts.cacheRead.toFixed(4)}
|
|
453
|
+
├─ output: $${realizedCosts.output.toFixed(4)}
|
|
454
|
+
└─ total: $${realizedCosts.total.toFixed(4)}
|
|
455
|
+
|
|
456
|
+
🌊 output
|
|
457
|
+
├─ logs: ${path.relative(cwd, logDir)}
|
|
458
|
+
└─ review: ${path.relative(cwd, outputAbsolute).startsWith('..') ? outputAbsolute : path.relative(cwd, outputAbsolute)}
|
|
459
|
+
`.trim(),
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
review: {
|
|
464
|
+
formatted: formattedReview,
|
|
465
|
+
},
|
|
466
|
+
log: {
|
|
467
|
+
dir: logDir,
|
|
468
|
+
},
|
|
469
|
+
output: {
|
|
470
|
+
path: outputAbsolute,
|
|
471
|
+
},
|
|
472
|
+
metrics: {
|
|
473
|
+
files: {
|
|
474
|
+
rulesCount: ruleFiles.length,
|
|
475
|
+
targetsCount: targetFiles.length,
|
|
476
|
+
},
|
|
477
|
+
expected: {
|
|
478
|
+
tokens: {
|
|
479
|
+
estimate: promptResult.tokenEstimate,
|
|
480
|
+
contextWindowPercent: promptResult.contextWindowPercent,
|
|
481
|
+
},
|
|
482
|
+
cost: {
|
|
483
|
+
estimate: promptResult.costEstimate,
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
realized: {
|
|
487
|
+
tokens: {
|
|
488
|
+
input: brainResult.usage.inputTokens,
|
|
489
|
+
inputCacheCreation: brainResult.usage.inputTokensCacheCreation,
|
|
490
|
+
inputCacheRead: brainResult.usage.inputTokensCacheRead,
|
|
491
|
+
output: brainResult.usage.outputTokens,
|
|
492
|
+
},
|
|
493
|
+
cost: {
|
|
494
|
+
input: realizedCosts.input,
|
|
495
|
+
cacheWrite: realizedCosts.cacheWrite,
|
|
496
|
+
cacheRead: realizedCosts.cacheRead,
|
|
497
|
+
output: realizedCosts.output,
|
|
498
|
+
total: realizedCosts.total,
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* .what = CLI entrypoint when run directly
|
|
507
|
+
* .why = enables ./review.sh to invoke this module
|
|
508
|
+
*/
|
|
509
|
+
if (require.main === module) {
|
|
510
|
+
// parse command line arguments
|
|
511
|
+
const args = process.argv.slice(2);
|
|
512
|
+
const parsed: Record<string, string> = {};
|
|
513
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
514
|
+
const key = args[i]?.replace(/^--/, '');
|
|
515
|
+
const value = args[i + 1];
|
|
516
|
+
if (key && value) parsed[key] = value;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// default output path if not provided
|
|
520
|
+
const output =
|
|
521
|
+
parsed.output ??
|
|
522
|
+
path.join(
|
|
523
|
+
process.cwd(),
|
|
524
|
+
'.review',
|
|
525
|
+
'bhrain',
|
|
526
|
+
`v${genLogTimestamp()}`,
|
|
527
|
+
'[feedback].[given].by_robot.md',
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
// validate mode is provided
|
|
531
|
+
if (!parsed.mode || (parsed.mode !== 'soft' && parsed.mode !== 'hard')) {
|
|
532
|
+
console.error('⛈️ error: --mode must be "soft" or "hard"');
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
const mode = parsed.mode as 'soft' | 'hard';
|
|
536
|
+
|
|
537
|
+
// default rules to .agent/**/briefs/**/rule.*.md
|
|
538
|
+
const rules = parsed.rules
|
|
539
|
+
? parsed.rules.split(',').map((r) => r.trim())
|
|
540
|
+
: ['.agent/**/briefs/**/rule.*.md'];
|
|
541
|
+
|
|
542
|
+
// default diffs to uptil-main
|
|
543
|
+
const diffs =
|
|
544
|
+
(parsed.diffs as 'uptil-main' | 'uptil-commit' | 'uptil-staged') ??
|
|
545
|
+
'uptil-main';
|
|
546
|
+
|
|
547
|
+
// parse paths if provided
|
|
548
|
+
const paths = parsed.paths
|
|
549
|
+
? parsed.paths.split(',').map((p) => p.trim())
|
|
550
|
+
: undefined;
|
|
551
|
+
|
|
552
|
+
// execute review
|
|
553
|
+
void (async () => {
|
|
554
|
+
try {
|
|
555
|
+
// ensure output directory exists
|
|
556
|
+
await fs.mkdir(path.dirname(output), { recursive: true });
|
|
557
|
+
|
|
558
|
+
// run the review
|
|
559
|
+
await stepReview({
|
|
560
|
+
rules,
|
|
561
|
+
diffs,
|
|
562
|
+
paths,
|
|
563
|
+
output,
|
|
564
|
+
mode,
|
|
565
|
+
});
|
|
566
|
+
} catch (error) {
|
|
567
|
+
if (error instanceof BadRequestError) {
|
|
568
|
+
console.error(`\n⛈️ error: ${error.message}`);
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
571
|
+
console.error('⛈️ unexpected error:', error);
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
})();
|
|
575
|
+
}
|
|
@@ -11,7 +11,7 @@ const stepDiverge_skill_1 = require("./skills/khue.diverge/stepDiverge.skill");
|
|
|
11
11
|
const stepInstantiate_skill_1 = require("./skills/khue.instantiate/stepInstantiate.skill");
|
|
12
12
|
const stepTriage_skill_1 = require("./skills/khue.triage/stepTriage.skill");
|
|
13
13
|
exports.ROLE_THINKER = rhachet_1.Role.build({
|
|
14
|
-
slug: '
|
|
14
|
+
slug: 'thinker',
|
|
15
15
|
name: 'Thinker',
|
|
16
16
|
purpose: 'think',
|
|
17
17
|
readme: `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getThinkerRole.js","sourceRoot":"","sources":["../../../src/roles/thinker/getThinkerRole.ts"],"names":[],"mappings":";;;AAAA,qCAA0C;AAE1C,uDAAoD;AACpD,yFAAkF;AAClF,yFAAkF;AAClF,4FAAqF;AACrF,+EAAwE;AACxE,+EAAwE;AACxE,2FAAoF;AACpF,4EAAqE;AAExD,QAAA,YAAY,GAAS,cAAI,CAAC,KAAK,CAAC;IAC3C,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"getThinkerRole.js","sourceRoot":"","sources":["../../../src/roles/thinker/getThinkerRole.ts"],"names":[],"mappings":";;;AAAA,qCAA0C;AAE1C,uDAAoD;AACpD,yFAAkF;AAClF,yFAAkF;AAClF,4FAAqF;AACrF,+EAAwE;AACxE,+EAAwE;AACxE,2FAAoF;AACpF,4EAAqE;AAExD,QAAA,YAAY,GAAS,cAAI,CAAC,KAAK,CAAC;IAC3C,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE;;;;GAIP,CAAC,IAAI,EAAE;IACR,MAAM,EAAE;QACN,mBAAS,CAAC,KAAK,CAAC;YACd,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,iDAAiD;YACzD,KAAK,EAAE,IAAA,iCAAe,EAAC,cAAc,CAAC;SACvC,CAAC;QACF,mBAAS,CAAC,KAAK,CAAC;YACd,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,8CAA8C;YACtD,KAAK,EAAE,IAAA,iCAAe,EAAC,0BAA0B,CAAC;SACnD,CAAC;KACH;IACD,MAAM,EAAE;QACN,IAAI,EAAE,EAAE;QACR,IAAI,EAAE;YACJ,kBAAkB;YAClB,iCAAa;YACb,iCAAa;YACb,+BAAY;YACZ,yCAAiB;YAEjB,kBAAkB;YAClB,uCAAgB;YAChB,yCAAiB;YACjB,uCAAgB;YAEhB,kBAAkB;YAClB,sCAAsC;SACvC;KACF;IACD,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;KACvC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 🧩 .brief: `vision.article`
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
a **vision** is a variant of an [article] that expresses a **desired future state**.
|
|
5
|
+
unlike descriptive or analytical articles, which capture what *is* or what *was*, a vision projects what *could be* or what *should be*.
|
|
6
|
+
it blends aspiration, imagination, and direction into a structured articulation.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🎯 purpose
|
|
11
|
+
- to **inspire** by painting a compelling picture of a possible future
|
|
12
|
+
- to **align** people, efforts, or resources toward a shared destination
|
|
13
|
+
- to **guide** decision-making by establishing a reference point for progress
|
|
14
|
+
- to **differentiate** from mere goals or plans by focusing on *direction and possibility*, not only steps
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ⚖️ comparison
|
|
19
|
+
|
|
20
|
+
| aspect | standard [article] | [vision].article |
|
|
21
|
+
|-------------------|-------------------------------------------|--------------------------------------------|
|
|
22
|
+
| **orientation** | present/past: what is, what was | future: what could/should be |
|
|
23
|
+
| **tone** | explanatory, descriptive, analytical | aspirational, imaginative, directional |
|
|
24
|
+
| **basis** | facts, evidence, analysis | ideals, possibilities, desired outcomes |
|
|
25
|
+
| **function** | inform, explain, clarify | inspire, align, mobilize |
|
|
26
|
+
| **risk** | incompleteness, bias, irrelevance | vagueness, over-idealization, impracticality|
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 📐 examples
|
|
31
|
+
|
|
32
|
+
- **article (education):** a report on literacy rates in rural schools.
|
|
33
|
+
- **vision.article (education):** “a future where every child, no matter their location, can access high-quality digital learning from home.”
|
|
34
|
+
|
|
35
|
+
- **article (healthcare):** documentation of hospital bed availability during flu season.
|
|
36
|
+
- **vision.article (healthcare):** “a world where predictive healthcare ensures no patient ever waits for urgent treatment.”
|
|
37
|
+
|
|
38
|
+
- **article (transportation):** a study of current urban traffic congestion.
|
|
39
|
+
- **vision.article (transportation):** “a city where clean, autonomous transit eliminates traffic jams and reduces commute times to minutes.”
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📊 insight
|
|
44
|
+
a **vision.article** sits at the crossroads of imagination and articulation:
|
|
45
|
+
- it frames the **direction of motion** without binding to the exact steps.
|
|
46
|
+
- it serves as a **north star** for other [articles] (plans, roadmaps, strategies).
|
|
47
|
+
- its strength lies in **clarity + aspiration**, balancing inspiration with plausibility.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 🧩 .brief: `[vision]`
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
a **vision** is an imaginative depiction or scenario illustrating a potential future state of a concept, entity, or project. it serves as a creative projection aimed at inspiring and guiding transformative thinking or strategic planning by showcasing what is possible.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🎯 purpose
|
|
9
|
+
- to **inspire innovation** and broad thinking about potential futures
|
|
10
|
+
- to **guide strategic decision-making** by providing a target state to work towards
|
|
11
|
+
- to **engage stakeholders** by vividly portraying the benefits and impact of pursuing the vision
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 🛠 components
|
|
16
|
+
|
|
17
|
+
### 1. **imagination**
|
|
18
|
+
- creatively constructed possibilities that envision a future state
|
|
19
|
+
- may involve speculative scenarios or conceptual explorations
|
|
20
|
+
|
|
21
|
+
### 2. **impact**
|
|
22
|
+
- describes the anticipated results or changes if the vision is realized
|
|
23
|
+
- typically highlights positive transformations or advancements
|
|
24
|
+
|
|
25
|
+
### 3. **feasibility**
|
|
26
|
+
- considers the practicality and potential challenges in achieving the vision
|
|
27
|
+
- often includes contingency thoughts to address uncertainties
|
|
28
|
+
|
|
29
|
+
### 4. **engagement**
|
|
30
|
+
- a compelling narrative that captures the interest of diverse audiences
|
|
31
|
+
- calls for alignment and participation from stakeholders
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🔍 features
|
|
36
|
+
- **vivid and illustrative** to make abstract ideas tangible
|
|
37
|
+
- **aspirational tone** to elevate thinking beyond current limitations
|
|
38
|
+
- includes **elements of storytelling** to foster emotional connection
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🏗 creating a vision
|
|
43
|
+
1. **research trends and scenarios**: understand ongoing changes and potential future shifts.
|
|
44
|
+
2. **brainstorm creatively**: think imaginatively about what is possible without current constraints.
|
|
45
|
+
3. **storyboard your vision**: craft a narrative that conveys the exciting, transformative potential.
|
|
46
|
+
4. **seek feedback and iteratively refine**: engage others in reviewing and enhancing the vision for broader resonance.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 📚 examples
|
|
51
|
+
- vision of a sustainable future city: "the city of tomorrow is a zero-emission urban paradise where nature and technology coexist, providing a thriving, inclusive, and adaptive ecosystem for all."
|
|
52
|
+
- vision for an education system: "an educational landscape where personalized learning and novel pedagogies empower every student to realize their full potential, unbounded by their starting point."
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 💡 notes
|
|
57
|
+
- a vision can be abstract and speculative, unlike a mission or strategy, which are typically more grounded and specific.
|
|
58
|
+
- ensure a vision remains **flexible** to evolve with new insights and realities.
|
|
59
|
+
|
|
60
|
+
---
|