strikethroo 3.0.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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +187 -0
- package/dist/cli.js.map +1 -0
- package/dist/conflict-detector.d.ts +24 -0
- package/dist/conflict-detector.d.ts.map +1 -0
- package/dist/conflict-detector.js +130 -0
- package/dist/conflict-detector.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +344 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata.d.ts +40 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +121 -0
- package/dist/metadata.js.map +1 -0
- package/dist/plan-utils.d.ts +35 -0
- package/dist/plan-utils.d.ts.map +1 -0
- package/dist/plan-utils.js +123 -0
- package/dist/plan-utils.js.map +1 -0
- package/dist/plan.d.ts +27 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +279 -0
- package/dist/plan.js.map +1 -0
- package/dist/prompts.d.ts +21 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +132 -0
- package/dist/prompts.js.map +1 -0
- package/dist/status.d.ts +73 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +375 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +274 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +53 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +158 -0
- package/dist/utils.js.map +1 -0
- package/package.json +94 -0
- package/templates/harness/agents/plan-creator.md +95 -0
- package/templates/harness/skills/st-create-plan/SKILL.md +120 -0
- package/templates/harness/skills/st-create-plan/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-create-plan/scripts/get-next-plan-id.cjs +197 -0
- package/templates/harness/skills/st-execute-blueprint/SKILL.md +147 -0
- package/templates/harness/skills/st-execute-blueprint/scripts/create-feature-branch.cjs +359 -0
- package/templates/harness/skills/st-execute-blueprint/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-execute-blueprint/scripts/validate-plan-blueprint.cjs +358 -0
- package/templates/harness/skills/st-execute-task/SKILL.md +195 -0
- package/templates/harness/skills/st-execute-task/scripts/check-task-dependencies.cjs +420 -0
- package/templates/harness/skills/st-execute-task/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-execute-task/scripts/validate-plan-blueprint.cjs +358 -0
- package/templates/harness/skills/st-full-workflow/SKILL.md +413 -0
- package/templates/harness/skills/st-full-workflow/scripts/create-feature-branch.cjs +359 -0
- package/templates/harness/skills/st-full-workflow/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-full-workflow/scripts/get-next-plan-id.cjs +197 -0
- package/templates/harness/skills/st-full-workflow/scripts/get-next-task-id.cjs +295 -0
- package/templates/harness/skills/st-full-workflow/scripts/validate-plan-blueprint.cjs +358 -0
- package/templates/harness/skills/st-generate-tasks/SKILL.md +244 -0
- package/templates/harness/skills/st-generate-tasks/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-generate-tasks/scripts/get-next-task-id.cjs +295 -0
- package/templates/harness/skills/st-generate-tasks/scripts/validate-plan-blueprint.cjs +358 -0
- package/templates/harness/skills/st-refine-plan/SKILL.md +205 -0
- package/templates/harness/skills/st-refine-plan/scripts/find-strikethroo-root.cjs +116 -0
- package/templates/harness/skills/st-refine-plan/scripts/validate-plan-blueprint.cjs +358 -0
- package/templates/strikethroo/README.md +7 -0
- package/templates/strikethroo/config/STRIKETHROO.md +76 -0
- package/templates/strikethroo/config/hooks/POST_ERROR_DETECTION.md +46 -0
- package/templates/strikethroo/config/hooks/POST_EXECUTION.md +25 -0
- package/templates/strikethroo/config/hooks/POST_PHASE.md +24 -0
- package/templates/strikethroo/config/hooks/POST_PLAN.md +5 -0
- package/templates/strikethroo/config/hooks/POST_TASK_GENERATION_ALL.md +41 -0
- package/templates/strikethroo/config/hooks/PRE_PHASE.md +21 -0
- package/templates/strikethroo/config/hooks/PRE_PLAN.md +42 -0
- package/templates/strikethroo/config/hooks/PRE_TASK_ASSIGNMENT.md +32 -0
- package/templates/strikethroo/config/hooks/PRE_TASK_EXECUTION.md +1 -0
- package/templates/strikethroo/config/templates/BLUEPRINT_TEMPLATE.md +24 -0
- package/templates/strikethroo/config/templates/EXECUTION_SUMMARY_TEMPLATE.md +13 -0
- package/templates/strikethroo/config/templates/PLAN_TEMPLATE.md +109 -0
- package/templates/strikethroo/config/templates/TASK_TEMPLATE.md +36 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/skill-scripts/validate-plan-blueprint.ts
|
|
31
|
+
var validate_plan_blueprint_exports = {};
|
|
32
|
+
__export(validate_plan_blueprint_exports, {
|
|
33
|
+
main: () => main
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(validate_plan_blueprint_exports);
|
|
36
|
+
var fs4 = __toESM(require("fs"));
|
|
37
|
+
var path4 = __toESM(require("path"));
|
|
38
|
+
|
|
39
|
+
// src/skill-scripts/shared/root.ts
|
|
40
|
+
var fs = __toESM(require("fs"));
|
|
41
|
+
var path = __toESM(require("path"));
|
|
42
|
+
var EXPECTED_SCHEMA = true ? 1 : 1;
|
|
43
|
+
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
44
|
+
try {
|
|
45
|
+
if (!fs.existsSync(strikethrooPath)) return false;
|
|
46
|
+
if (!fs.lstatSync(strikethrooPath).isDirectory()) return false;
|
|
47
|
+
const metadataPath = path.join(strikethrooPath, ".init-metadata.json");
|
|
48
|
+
if (!fs.existsSync(metadataPath)) return false;
|
|
49
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
50
|
+
return metadata && typeof metadata === "object" && "version" in metadata;
|
|
51
|
+
} catch (_err) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var getStrikethrooAt = (directory) => {
|
|
56
|
+
const strikethrooPath = path.join(directory, ".ai", "strikethroo");
|
|
57
|
+
return isValidStrikethrooRoot(strikethrooPath) ? strikethrooPath : null;
|
|
58
|
+
};
|
|
59
|
+
var getParentPaths = (currentPath, acc = []) => {
|
|
60
|
+
const absolutePath = path.resolve(currentPath);
|
|
61
|
+
const nextAcc = [...acc, absolutePath];
|
|
62
|
+
const parentPath = path.dirname(absolutePath);
|
|
63
|
+
if (parentPath === absolutePath) return nextAcc;
|
|
64
|
+
return getParentPaths(parentPath, nextAcc);
|
|
65
|
+
};
|
|
66
|
+
var checkWorkspaceSchema = (metadataPath) => {
|
|
67
|
+
let metadata;
|
|
68
|
+
try {
|
|
69
|
+
metadata = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
70
|
+
} catch {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const actual = typeof metadata.workspaceSchemaVersion === "number" ? metadata.workspaceSchemaVersion : 1;
|
|
74
|
+
if (actual === EXPECTED_SCHEMA) return;
|
|
75
|
+
if (actual < EXPECTED_SCHEMA) {
|
|
76
|
+
process.stderr.write(
|
|
77
|
+
`Workspace schema v${actual} is older than this skill requires (v${EXPECTED_SCHEMA}). Re-run \`npx strikethroo init\` with the latest CLI to update.
|
|
78
|
+
`
|
|
79
|
+
);
|
|
80
|
+
} else {
|
|
81
|
+
process.stderr.write(
|
|
82
|
+
`This skill (built for workspace schema v${EXPECTED_SCHEMA}) is older than the workspace (v${actual}). Re-run \`npx skills add e0ipso/strikethroo\` to update skills.
|
|
83
|
+
`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
process.exit(1);
|
|
87
|
+
};
|
|
88
|
+
var findStrikethrooRoot = (startPath = process.cwd()) => {
|
|
89
|
+
const paths = getParentPaths(startPath);
|
|
90
|
+
const found = paths.find((p) => getStrikethrooAt(p));
|
|
91
|
+
if (!found) return null;
|
|
92
|
+
const root = getStrikethrooAt(found);
|
|
93
|
+
if (root) checkWorkspaceSchema(path.join(root, ".init-metadata.json"));
|
|
94
|
+
return root;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/skill-scripts/shared/plan-scan.ts
|
|
98
|
+
var fs2 = __toESM(require("fs"));
|
|
99
|
+
var path2 = __toESM(require("path"));
|
|
100
|
+
|
|
101
|
+
// src/skill-scripts/shared/frontmatter.ts
|
|
102
|
+
var ID_PATTERNS = [
|
|
103
|
+
/^\s*["']?id["']?\s*:\s*["']?([+-]?\d+)["']?\s*(?:#.*)?$/im,
|
|
104
|
+
/^\s*id\s*:\s*([+-]?\d+)\s*(?:#.*)?$/im,
|
|
105
|
+
/^\s*["']?id["']?\s*:\s*"([+-]?\d+)"\s*(?:#.*)?$/im,
|
|
106
|
+
/^\s*["']?id["']?\s*:\s*'([+-]?\d+)'\s*(?:#.*)?$/im,
|
|
107
|
+
/^\s*["']id["']\s*:\s*([+-]?\d+)\s*(?:#.*)?$/im,
|
|
108
|
+
/^\s*id\s*:\s*[|>]\s*([+-]?\d+)\s*$/im
|
|
109
|
+
];
|
|
110
|
+
var validateId = (rawId) => {
|
|
111
|
+
const id = parseInt(rawId, 10);
|
|
112
|
+
if (Number.isNaN(id) || id < 0 || id > Number.MAX_SAFE_INTEGER) return null;
|
|
113
|
+
return id;
|
|
114
|
+
};
|
|
115
|
+
var extractIdFromMarkdown = (content) => {
|
|
116
|
+
const frontmatterMatch = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
117
|
+
if (!frontmatterMatch || !frontmatterMatch[1]) return null;
|
|
118
|
+
const block = frontmatterMatch[1];
|
|
119
|
+
for (const pattern of ID_PATTERNS) {
|
|
120
|
+
const match = block.match(pattern);
|
|
121
|
+
if (match && match[1]) {
|
|
122
|
+
const id = validateId(match[1]);
|
|
123
|
+
if (id !== null) return id;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
};
|
|
128
|
+
var extractPlanId = (content, _filePath) => {
|
|
129
|
+
return extractIdFromMarkdown(content);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/skill-scripts/shared/plan-scan.ts
|
|
133
|
+
var PLAN_EXTENSIONS = [".md"];
|
|
134
|
+
var scanPlanDir = (planDirPath, dirName, isArchive) => {
|
|
135
|
+
let entries;
|
|
136
|
+
try {
|
|
137
|
+
entries = fs2.readdirSync(planDirPath, { withFileTypes: true });
|
|
138
|
+
} catch (_err) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
return entries.filter((e) => e.isFile() && PLAN_EXTENSIONS.some((ext) => e.name.endsWith(ext))).flatMap((e) => {
|
|
142
|
+
const filePath = path2.join(planDirPath, e.name);
|
|
143
|
+
try {
|
|
144
|
+
const content = fs2.readFileSync(filePath, "utf8");
|
|
145
|
+
const id = extractPlanId(content, filePath);
|
|
146
|
+
if (id === null) return [];
|
|
147
|
+
return [{ id, file: filePath, dir: planDirPath, isArchive, name: dirName }];
|
|
148
|
+
} catch (_err) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
var getAllPlans = (taskManagerRoot) => {
|
|
154
|
+
const sources = [
|
|
155
|
+
{ dir: path2.join(taskManagerRoot, "plans"), isArchive: false },
|
|
156
|
+
{ dir: path2.join(taskManagerRoot, "archive"), isArchive: true }
|
|
157
|
+
];
|
|
158
|
+
return sources.flatMap(({ dir, isArchive }) => {
|
|
159
|
+
if (!fs2.existsSync(dir)) return [];
|
|
160
|
+
let entries;
|
|
161
|
+
try {
|
|
162
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
163
|
+
} catch (_err) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
return entries.filter((e) => e.isDirectory()).flatMap((e) => scanPlanDir(path2.join(dir, e.name), e.name, isArchive));
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/skill-scripts/shared/plan-resolve.ts
|
|
171
|
+
var fs3 = __toESM(require("fs"));
|
|
172
|
+
var path3 = __toESM(require("path"));
|
|
173
|
+
var isValidRootDir = (strikethrooPath) => {
|
|
174
|
+
try {
|
|
175
|
+
if (!fs3.existsSync(strikethrooPath)) return false;
|
|
176
|
+
if (!fs3.lstatSync(strikethrooPath).isDirectory()) return false;
|
|
177
|
+
const metadataPath = path3.join(strikethrooPath, ".init-metadata.json");
|
|
178
|
+
if (!fs3.existsSync(metadataPath)) return false;
|
|
179
|
+
const metadata = JSON.parse(fs3.readFileSync(metadataPath, "utf8"));
|
|
180
|
+
return metadata && typeof metadata === "object" && "version" in metadata;
|
|
181
|
+
} catch (_err) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var checkStandardRootShortcut = (filePath) => {
|
|
186
|
+
const planDir = path3.dirname(filePath);
|
|
187
|
+
const parentDir = path3.dirname(planDir);
|
|
188
|
+
const possibleRoot = path3.dirname(parentDir);
|
|
189
|
+
const parentBase = path3.basename(parentDir);
|
|
190
|
+
if (parentBase !== "plans" && parentBase !== "archive") return null;
|
|
191
|
+
if (path3.basename(possibleRoot) !== "strikethroo") return null;
|
|
192
|
+
const dotAiDir = path3.dirname(possibleRoot);
|
|
193
|
+
if (path3.basename(dotAiDir) !== ".ai") return null;
|
|
194
|
+
return isValidRootDir(possibleRoot) ? possibleRoot : null;
|
|
195
|
+
};
|
|
196
|
+
var resolveByPath = (absolutePath) => {
|
|
197
|
+
let content;
|
|
198
|
+
try {
|
|
199
|
+
content = fs3.readFileSync(absolutePath, "utf8");
|
|
200
|
+
} catch (_err) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const planId = extractPlanId(content, absolutePath);
|
|
204
|
+
if (planId === null) return null;
|
|
205
|
+
const tmRoot = checkStandardRootShortcut(absolutePath) || findStrikethrooRoot(path3.dirname(absolutePath));
|
|
206
|
+
if (!tmRoot) return null;
|
|
207
|
+
return {
|
|
208
|
+
planFile: absolutePath,
|
|
209
|
+
planDir: path3.dirname(absolutePath),
|
|
210
|
+
strikethrooRoot: tmRoot,
|
|
211
|
+
planId
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
var resolveByIdInAncestry = (planId, startPath, searched = /* @__PURE__ */ new Set()) => {
|
|
215
|
+
const tmRoot = findStrikethrooRoot(startPath);
|
|
216
|
+
if (!tmRoot) return null;
|
|
217
|
+
const normalized = path3.normalize(tmRoot);
|
|
218
|
+
if (searched.has(normalized)) return null;
|
|
219
|
+
searched.add(normalized);
|
|
220
|
+
const plans = getAllPlans(tmRoot);
|
|
221
|
+
const match = plans.find((p) => p.id === planId);
|
|
222
|
+
if (match) {
|
|
223
|
+
return {
|
|
224
|
+
planFile: match.file,
|
|
225
|
+
planDir: match.dir,
|
|
226
|
+
strikethrooRoot: tmRoot,
|
|
227
|
+
planId
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const parentOfRoot = path3.dirname(path3.dirname(tmRoot));
|
|
231
|
+
if (parentOfRoot === tmRoot) return null;
|
|
232
|
+
return resolveByIdInAncestry(planId, parentOfRoot, searched);
|
|
233
|
+
};
|
|
234
|
+
var resolvePlan = (input, startPath = process.cwd()) => {
|
|
235
|
+
if (input === null || input === void 0 || input === "") return null;
|
|
236
|
+
const inputStr = String(input);
|
|
237
|
+
if (inputStr.startsWith("/")) {
|
|
238
|
+
return resolveByPath(inputStr);
|
|
239
|
+
}
|
|
240
|
+
const planId = parseInt(inputStr, 10);
|
|
241
|
+
if (Number.isNaN(planId)) return null;
|
|
242
|
+
return resolveByIdInAncestry(planId, startPath);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// src/skill-scripts/validate-plan-blueprint.ts
|
|
246
|
+
var VALID_FIELDS = [
|
|
247
|
+
"planFile",
|
|
248
|
+
"planDir",
|
|
249
|
+
"taskCount",
|
|
250
|
+
"blueprintExists",
|
|
251
|
+
"strikethrooRoot",
|
|
252
|
+
"planId"
|
|
253
|
+
];
|
|
254
|
+
var countTasks = (planDir) => {
|
|
255
|
+
const tasksDir = path4.join(planDir, "tasks");
|
|
256
|
+
if (!fs4.existsSync(tasksDir)) return 0;
|
|
257
|
+
try {
|
|
258
|
+
const stat = fs4.lstatSync(tasksDir);
|
|
259
|
+
if (!stat.isDirectory()) return 0;
|
|
260
|
+
return fs4.readdirSync(tasksDir).filter((f) => f.endsWith(".md")).length;
|
|
261
|
+
} catch (_err) {
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
var checkBlueprintExists = (planFile) => {
|
|
266
|
+
try {
|
|
267
|
+
const content = fs4.readFileSync(planFile, "utf8");
|
|
268
|
+
return /^## Execution Blueprint/m.test(content);
|
|
269
|
+
} catch (_err) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var usage = () => {
|
|
274
|
+
const lines = [
|
|
275
|
+
"Plan ID or absolute path is required",
|
|
276
|
+
"",
|
|
277
|
+
"Usage: node validate-plan-blueprint.cjs <plan-id-or-path> [field-name]",
|
|
278
|
+
"",
|
|
279
|
+
"Examples:",
|
|
280
|
+
" node validate-plan-blueprint.cjs 47",
|
|
281
|
+
" node validate-plan-blueprint.cjs /path/to/plan.md",
|
|
282
|
+
" node validate-plan-blueprint.cjs 47 planFile",
|
|
283
|
+
" node validate-plan-blueprint.cjs 47 blueprintExists"
|
|
284
|
+
];
|
|
285
|
+
lines.forEach((l) => process.stderr.write(`[ERROR] ${l}
|
|
286
|
+
`));
|
|
287
|
+
};
|
|
288
|
+
var listAvailablePlans = (startPath) => {
|
|
289
|
+
const tmRoot = findStrikethrooRoot(startPath);
|
|
290
|
+
if (!tmRoot) return [];
|
|
291
|
+
const plans = getAllPlans(tmRoot);
|
|
292
|
+
return plans.map((p) => p.name).sort((a, b) => {
|
|
293
|
+
const aMatch = a.match(/^(\d+)--/);
|
|
294
|
+
const bMatch = b.match(/^(\d+)--/);
|
|
295
|
+
if (!aMatch || !bMatch || !aMatch[1] || !bMatch[1]) return 0;
|
|
296
|
+
return parseInt(aMatch[1], 10) - parseInt(bMatch[1], 10);
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
var main = () => {
|
|
300
|
+
const inputId = process.argv[2];
|
|
301
|
+
const fieldName = process.argv[3];
|
|
302
|
+
if (!inputId) {
|
|
303
|
+
usage();
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
const numericInput = parseInt(inputId, 10);
|
|
307
|
+
const isNumeric = !Number.isNaN(numericInput);
|
|
308
|
+
const isAbsolutePath = inputId.startsWith("/");
|
|
309
|
+
if (!isNumeric && !isAbsolutePath) {
|
|
310
|
+
process.stderr.write(`[ERROR] Invalid plan ID: "${inputId}" is not a valid number
|
|
311
|
+
`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
const resolved = resolvePlan(inputId);
|
|
315
|
+
if (!resolved) {
|
|
316
|
+
process.stderr.write(`[ERROR] Plan ID ${inputId} not found or invalid
|
|
317
|
+
`);
|
|
318
|
+
process.stderr.write("[ERROR] \n");
|
|
319
|
+
const available = listAvailablePlans(process.cwd());
|
|
320
|
+
if (available.length > 0) {
|
|
321
|
+
process.stderr.write("[ERROR] Available plans:\n");
|
|
322
|
+
available.forEach((name) => process.stderr.write(`[ERROR] ${name}
|
|
323
|
+
`));
|
|
324
|
+
}
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
const result = {
|
|
328
|
+
planFile: resolved.planFile,
|
|
329
|
+
planDir: resolved.planDir,
|
|
330
|
+
strikethrooRoot: resolved.strikethrooRoot,
|
|
331
|
+
planId: resolved.planId,
|
|
332
|
+
taskCount: countTasks(resolved.planDir),
|
|
333
|
+
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no"
|
|
334
|
+
};
|
|
335
|
+
if (fieldName) {
|
|
336
|
+
if (!VALID_FIELDS.includes(fieldName)) {
|
|
337
|
+
process.stderr.write(`[ERROR] Invalid field name: ${fieldName}
|
|
338
|
+
`);
|
|
339
|
+
process.stderr.write(`[ERROR] Valid fields: ${VALID_FIELDS.join(", ")}
|
|
340
|
+
`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
const value = result[fieldName];
|
|
344
|
+
process.stdout.write(`${String(value)}
|
|
345
|
+
`);
|
|
346
|
+
} else {
|
|
347
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
348
|
+
`);
|
|
349
|
+
}
|
|
350
|
+
process.exit(0);
|
|
351
|
+
};
|
|
352
|
+
if (require.main === module) {
|
|
353
|
+
main();
|
|
354
|
+
}
|
|
355
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
356
|
+
0 && (module.exports = {
|
|
357
|
+
main
|
|
358
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Strikethroo General Information
|
|
2
|
+
|
|
3
|
+
This document contains important information that is common to all the
|
|
4
|
+
Strikethroo skills your AI assistant uses to plan, decompose, and execute work.
|
|
5
|
+
|
|
6
|
+
## Types of Documents
|
|
7
|
+
|
|
8
|
+
Work orders (abbreviated as WO) are complex prompts for programming,
|
|
9
|
+
organizational, or management tasks created by a user. Work orders are
|
|
10
|
+
independent of each other and cannot share any context. By definition
|
|
11
|
+
different work orders can be worked on independently.
|
|
12
|
+
|
|
13
|
+
Each work order has plan associated to it. The plan is a comprehensive document
|
|
14
|
+
highlighting all the aspects of the work necessary to accomplish the goals from
|
|
15
|
+
the work order.
|
|
16
|
+
|
|
17
|
+
Each plan will be broken into tasks. Each task is a logical unit of work that
|
|
18
|
+
has a single purpose, and is solved using a single skill. All tasks exist as
|
|
19
|
+
part of a plan. Tasks can have dependencies on other tasks. This happens when a
|
|
20
|
+
task cannot be worked on (or completed) before some other task(s) are completed.
|
|
21
|
+
|
|
22
|
+
## Directory Structure
|
|
23
|
+
|
|
24
|
+
To find a plan document from its ID use the following command (replace {planId} with the ID, like 06):
|
|
25
|
+
```shell
|
|
26
|
+
find .ai/strikethroo/{plans,archive} -name "plan-[0-9][0-9]*--*.md" -type f -exec grep -l "^id: \?{planId}$" {} \;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Plans, and tasks are stored as MarkDown files with a YAML front-matter. They are
|
|
30
|
+
all filed under the `.ai/strikethroo/` folder at the root of the repository.
|
|
31
|
+
|
|
32
|
+
Plans are organized as follows:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
.ai/
|
|
36
|
+
strikethroo/
|
|
37
|
+
plans/ # Active plans (work in progress)
|
|
38
|
+
01--authentication-provider/
|
|
39
|
+
plan-01--authentication-provider.md
|
|
40
|
+
tasks/
|
|
41
|
+
01--create-project-structure.md
|
|
42
|
+
02--implement-authorization.md
|
|
43
|
+
03--this-example-task.md
|
|
44
|
+
04--create-tests.md
|
|
45
|
+
05--update-documentation.md
|
|
46
|
+
archive/ # Completed plans (successfully executed)
|
|
47
|
+
05--user-management/
|
|
48
|
+
plan-05--user-management.md
|
|
49
|
+
tasks/
|
|
50
|
+
01--create-user-model.md
|
|
51
|
+
02--implement-crud-operations.md
|
|
52
|
+
03--add-validation.md
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Note how in the `.ai/strikethroo/plans/` folder we have a sub-folder per plan.
|
|
56
|
+
Each sub-folder will contain the plan document and has a name following a naming
|
|
57
|
+
pattern `[ID]--[plan-short-name]`. Where the ID is auto-incremental. The plan
|
|
58
|
+
document has a name following the pattern `plan-[ID]--[plan-short-name].md`.
|
|
59
|
+
Finally, all tasks are under a `tasks` sub-folder. Each task has a name
|
|
60
|
+
according to the pattern `[incremental-ID]--[task-short-name].md`. IDs for tasks
|
|
61
|
+
are auto-incremental within a plan. Each plan starts their tasks' IDs from 01.
|
|
62
|
+
|
|
63
|
+
## Plan Lifecycle and Archive System
|
|
64
|
+
|
|
65
|
+
Plans follow a lifecycle that maintains workspace organization:
|
|
66
|
+
|
|
67
|
+
1. **Active Plans**: When created, plans are placed in the `plans/` directory where they remain while being worked on.
|
|
68
|
+
|
|
69
|
+
2. **Completed Plans**: Upon successful execution of a blueprint (via the `st-execute-blueprint` skill), the entire plan directory is automatically moved from `plans/` to `archive/`.
|
|
70
|
+
|
|
71
|
+
3. **Archive Directory**: The `archive/` directory serves as permanent storage for completed work. This separation keeps the active workspace clean while preserving completed plans for reference.
|
|
72
|
+
|
|
73
|
+
The archive system provides several benefits:
|
|
74
|
+
- **Workspace Organization**: Active plans remain easily accessible while completed work doesn't clutter the workspace
|
|
75
|
+
- **Historical Reference**: Completed plans and their tasks remain available for future reference or learning
|
|
76
|
+
- **Automatic Management**: No manual intervention required - archival happens automatically upon successful completion
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# POST_ERROR_DETECTION Hook
|
|
2
|
+
|
|
3
|
+
## Task Execution Error Handling
|
|
4
|
+
|
|
5
|
+
If task execution fails:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# On execution failure, update status to failed
|
|
9
|
+
echo "Task execution failed - updating status..."
|
|
10
|
+
|
|
11
|
+
TEMP_FILE=$(mktemp)
|
|
12
|
+
awk '
|
|
13
|
+
/^---$/ {
|
|
14
|
+
if (++delim == 1) {
|
|
15
|
+
print
|
|
16
|
+
next
|
|
17
|
+
} else if (delim == 2) {
|
|
18
|
+
print "status: \"failed\""
|
|
19
|
+
print
|
|
20
|
+
next
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/^status:/ && delim == 1 {
|
|
24
|
+
print "status: \"failed\""
|
|
25
|
+
next
|
|
26
|
+
}
|
|
27
|
+
{ print }
|
|
28
|
+
' "$TASK_FILE" > "$TEMP_FILE"
|
|
29
|
+
|
|
30
|
+
mv "$TEMP_FILE" "$TASK_FILE"
|
|
31
|
+
|
|
32
|
+
echo "Task ${TASK_ID} marked as failed"
|
|
33
|
+
echo "Check the task requirements and try again"
|
|
34
|
+
exit 1
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Validation Gate Failure Handling
|
|
38
|
+
|
|
39
|
+
#### Validation Gate Failures
|
|
40
|
+
If validation gates fail:
|
|
41
|
+
1. Document which specific validations failed
|
|
42
|
+
2. Identify which tasks may have caused the failure
|
|
43
|
+
3. Generate remediation plan
|
|
44
|
+
4. Re-execute affected tasks after fixes
|
|
45
|
+
5. Re-run validation gates
|
|
46
|
+
6. If errors persist, escalate to the user
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# POST_EXECUTION Hook
|
|
2
|
+
|
|
3
|
+
## Validation Gates
|
|
4
|
+
|
|
5
|
+
Before marking the blueprint as complete, verify:
|
|
6
|
+
|
|
7
|
+
- [ ] All linting rules must pass without errors. If no linter is configured, skip this step
|
|
8
|
+
- [ ] All tests must pass successfully. If no test suite is configured, skip this step
|
|
9
|
+
- [ ] Verify all tasks in the plan have `status: "completed"` in their frontmatter
|
|
10
|
+
- [ ] Verify that the AGENTS.md documentation or related documentes are still correct after this plan execution
|
|
11
|
+
- [ ] Execute the **Self Validation** steps defined in the plan document. These are concrete verification procedures (e.g., Playwright browser checks, database CLI queries, screenshots) that confirm the implementation works in the real system. If any step fails, treat it as a validation gate failure
|
|
12
|
+
|
|
13
|
+
## Cleanup
|
|
14
|
+
Assess weather the current plan has left tech debt behind or if it has created dead code. None of those are acceptable. Fix them and leave behind the most maintainable code change possible.
|
|
15
|
+
|
|
16
|
+
Backwards compatibility and legacy support are only tolerated if explicitly expressed by the user. Unless it is called out in the plan, you are to assume that backwards compatibility layers are tech debt that should be eliminated.
|
|
17
|
+
|
|
18
|
+
## Failure Behavior
|
|
19
|
+
|
|
20
|
+
If any validation gate fails:
|
|
21
|
+
|
|
22
|
+
- **Halt execution immediately** - do not proceed to summary generation or archival
|
|
23
|
+
- **Leave plan in `plans/` directory** for debugging and correction
|
|
24
|
+
- **Document the failure** in the plan file with details about which gate failed
|
|
25
|
+
- **Provide actionable next steps** for resolving the failure
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# POST_PHASE Hook
|
|
2
|
+
|
|
3
|
+
Ensure that:
|
|
4
|
+
|
|
5
|
+
- The code base is passing the linting requirements
|
|
6
|
+
- A descriptive commit (using conventional commits with a subject and a description) for the phase was successfully created.
|
|
7
|
+
|
|
8
|
+
### Execution Monitoring
|
|
9
|
+
|
|
10
|
+
#### Progress Tracking
|
|
11
|
+
|
|
12
|
+
Update the list of tasks from the plan document to add the status of each task
|
|
13
|
+
and phase. Once a phase has been completed and validated, and before you move to
|
|
14
|
+
the next phase, update the blueprint and add a ✅ emoji in front of its title.
|
|
15
|
+
Add ✔️ emoji in front of all the tasks in that phase, and update their status to
|
|
16
|
+
`completed`.
|
|
17
|
+
|
|
18
|
+
#### Task Status Updates
|
|
19
|
+
Valid status transitions:
|
|
20
|
+
- `pending` → `in-progress` (when agent starts)
|
|
21
|
+
- `in-progress` → `completed` (successful execution)
|
|
22
|
+
- `in-progress` → `failed` (execution error)
|
|
23
|
+
- `failed` → `in-progress` (retry attempt)
|
|
24
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# POST_TASK_GENERATION_ALL Hook
|
|
2
|
+
|
|
3
|
+
After all tasks have been generated, perform these two steps:
|
|
4
|
+
|
|
5
|
+
## 1. Review Task Complexity
|
|
6
|
+
|
|
7
|
+
For each generated task, do a quick sanity check:
|
|
8
|
+
|
|
9
|
+
- **Too complex?** If a task spans 3+ technologies or requires 3+ skills, split it.
|
|
10
|
+
- **Too vague?** If acceptance criteria are unclear, sharpen them.
|
|
11
|
+
- **Too trivial?** If two tasks could be one without adding complexity, merge them.
|
|
12
|
+
|
|
13
|
+
Target: every task should be completable with 1-2 skills and have clear acceptance criteria.
|
|
14
|
+
|
|
15
|
+
## 2. Update Plan with Blueprint
|
|
16
|
+
|
|
17
|
+
After finalizing tasks, append to the plan document:
|
|
18
|
+
|
|
19
|
+
### Dependency Diagram
|
|
20
|
+
|
|
21
|
+
If tasks have dependencies, add a Mermaid graph:
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph TD
|
|
25
|
+
001[Task 001: Description] --> 002[Task 002: Description]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Verify there are no circular dependencies.
|
|
29
|
+
|
|
30
|
+
### Execution Phases
|
|
31
|
+
|
|
32
|
+
Group tasks into phases:
|
|
33
|
+
- **Phase 1**: Tasks with no dependencies (run in parallel)
|
|
34
|
+
- **Phase N**: Tasks whose dependencies are all in earlier phases
|
|
35
|
+
|
|
36
|
+
Use the template in `.ai/strikethroo/config/templates/BLUEPRINT_TEMPLATE.md` for structure.
|
|
37
|
+
|
|
38
|
+
Before finalizing, verify:
|
|
39
|
+
- Every task is in exactly one phase
|
|
40
|
+
- No task runs before its dependencies complete
|
|
41
|
+
- Phase 1 has only zero-dependency tasks
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# PRE_PHASE Hook
|
|
2
|
+
|
|
3
|
+
## Phase Pre-Execution
|
|
4
|
+
|
|
5
|
+
### Feature Branch Creation
|
|
6
|
+
|
|
7
|
+
Create a feature branch for this plan execution:
|
|
8
|
+
|
|
9
|
+
- From `main`/`master` with a clean working tree: create a branch named `feature/{planId}--{plan-name}` and switch to it.
|
|
10
|
+
- From `main`/`master` with uncommitted changes: halt with an error — do not proceed.
|
|
11
|
+
- Already on a feature branch: proceed without creating a new branch.
|
|
12
|
+
- Branch already exists: switch to it and proceed normally.
|
|
13
|
+
|
|
14
|
+
## Phase Execution Workflow
|
|
15
|
+
|
|
16
|
+
1. **Phase Initialization**
|
|
17
|
+
- Identify current phase from the execution blueprint
|
|
18
|
+
- List all tasks scheduled for parallel execution in this phase
|
|
19
|
+
- **Validate Task Dependencies**: For each task in the current phase, verify that all declared dependencies (from the task's YAML frontmatter `dependencies` array) have status `completed`. If any dependency is unresolved, halt the phase and report the blocking dependencies before continuing.
|
|
20
|
+
- Confirm no tasks are marked "needs-clarification"
|
|
21
|
+
- If any phases are marked as completed, verify they are actually completed and continue from the next phase.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# PRE_PLAN Hook
|
|
2
|
+
|
|
3
|
+
## Scope Control Guidelines
|
|
4
|
+
|
|
5
|
+
**Critical: Implement ONLY what is explicitly requested**
|
|
6
|
+
|
|
7
|
+
- **Minimal Viable Implementation**: Build exactly what the user asked for, nothing more
|
|
8
|
+
- **Question Everything Extra**: If not directly mentioned by the user, don't add it
|
|
9
|
+
- **Avoid Feature Creep**: Resist the urge to add "helpful" features or "nice-to-have" additions
|
|
10
|
+
- **YAGNI Principle**: _You Aren't Gonna Need It_ - don't build for hypothetical future needs
|
|
11
|
+
- **Do NOT add backwards compatibility, unless requested**: If there is a potential BC break, ask the user if they want to BC support. Do not assume the want it.
|
|
12
|
+
|
|
13
|
+
**Common Scope Creep Anti-Patterns to Avoid:**
|
|
14
|
+
1. Adding extra commands or features "for completeness"
|
|
15
|
+
2. Creating infrastructure for future features that weren't requested
|
|
16
|
+
3. Building abstractions or frameworks when simple solutions suffice
|
|
17
|
+
4. Adding configuration options not specifically mentioned
|
|
18
|
+
5. Implementing error handling beyond what's necessary for the core request
|
|
19
|
+
6. Creating documentation or help systems unless explicitly requested
|
|
20
|
+
|
|
21
|
+
**When in doubt, ask**: "Is this feature explicitly mentioned in the user's request?"
|
|
22
|
+
|
|
23
|
+
## Simplicity Principles
|
|
24
|
+
|
|
25
|
+
**Favor maintainability over cleverness**
|
|
26
|
+
|
|
27
|
+
- **Simple Solutions First**: Choose the most straightforward approach that meets requirements
|
|
28
|
+
- **Avoid Over-Engineering**: Don't create complex systems when simple ones work
|
|
29
|
+
- **Readable Code**: Write code that others can easily understand and modify
|
|
30
|
+
- **Standard Patterns**: Use established patterns rather than inventing new ones
|
|
31
|
+
- **Minimal Dependencies**: Add external dependencies only when essential, but do not re-invent the wheel
|
|
32
|
+
- **Clear Structure**: Organize code in obvious, predictable ways
|
|
33
|
+
|
|
34
|
+
**Remember**: A working simple solution is better than a complex "perfect" one.
|
|
35
|
+
|
|
36
|
+
## Critical Notes
|
|
37
|
+
|
|
38
|
+
- Never generate a partial or assumed plan without adequate context
|
|
39
|
+
- Prioritize accuracy over speed
|
|
40
|
+
- Consider both technical and non-technical aspects
|
|
41
|
+
- Use the plan template in .ai/strikethroo/config/templates/PLAN_TEMPLATE.md
|
|
42
|
+
- DO NOT create or list any tasks or phases during the plan creation. This will be done in a later step. Stick to writing the PRD (Project Requirements Document).
|