@jaybeeuu/agent-cortex 2.1.0 → 2.2.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/CHANGELOG.md +387 -0
- package/README.md +33 -13
- package/agents/README.md +22 -10
- package/agents/plan/agent.md +2 -2
- package/agents/ralph/agent.md +126 -88
- package/agents/ralph/claude/frontmatter.json +3 -3
- package/agents/ralph/claude/wake.md +46 -0
- package/agents/ralph/pi/{polling.md → wake.md} +35 -20
- package/agents/ralph-plan/agent.md +23 -11
- package/bin/agent-cortex.mjs +66 -4
- package/bin/installers/claude.mjs +182 -68
- package/bin/installers/pi.mjs +200 -24
- package/extensions/agent-guardrails/README.md +1 -1
- package/extensions/agent-guardrails/guardrails.ts +1 -1
- package/extensions/agent-guardrails/index.ts +1 -1
- package/extensions/agent-modes/discover.test.ts +2 -2
- package/hooks/claude/scripts/notify.mjs +8 -1
- package/lib/cli.mjs +91 -2
- package/package.json +27 -3
- package/scripts/check-changeset.mjs +4 -0
- package/scripts/check-no-sync.mjs +5 -1
- package/scripts/lib/compose-agent.mjs +69 -9
- package/scripts/lib/fs.mjs +10 -2
- package/scripts/lib/migrate.mjs +736 -0
- package/scripts/lib/release-gates.mjs +21 -1
- package/scripts/lib/skill-frontmatter.mjs +23 -6
- package/scripts/lib/skill-links.mjs +52 -0
- package/scripts/lib/verify-worker-tree.mjs +25 -0
- package/scripts/resolve-publish-command.mjs +4 -0
- package/skills/engineering/design-an-interface/SKILL.md +3 -3
- package/skills/engineering/grill-with-docs/SKILL.md +3 -5
- package/skills/engineering/improve-codebase-architecture/SKILL.md +5 -4
- package/skills/engineering/tdd/SKILL.md +5 -7
- package/skills/engineering/technical-direction/SKILL.md +5 -5
- package/skills/planning/classify-bead/REFERENCE.md +3 -3
- package/skills/planning/classify-bead/SKILL.md +14 -9
- package/skills/planning/classify-bead/scripts/classify-bead.mjs +5 -4
- package/skills/planning/create-task/REFERENCE.md +52 -3
- package/skills/planning/create-task/SKILL.md +70 -26
- package/skills/planning/create-task/scripts/{create-chores.ts → create-chores.mjs} +48 -23
- package/skills/planning/flesh-out-idea/REFERENCE.md +116 -0
- package/skills/planning/flesh-out-idea/SKILL.md +146 -0
- package/skills/planning/prd-to-tasks/REFERENCE.md +54 -5
- package/skills/planning/prd-to-tasks/SKILL.md +22 -16
- package/skills/planning/record-idea/FORMAT.md +66 -17
- package/skills/planning/record-idea/SKILL.md +48 -24
- package/skills/planning/request-refactor-plan/SKILL.md +4 -4
- package/skills/planning/spec-to-tickets/SKILL.md +5 -5
- package/skills/planning/write-a-prd/SKILL.md +8 -9
- package/skills/planning/write-a-ticket/SKILL.md +4 -6
- package/skills/productivity/bd-tool/SKILL.md +15 -10
- package/skills/productivity/grill-me/SKILL.md +5 -0
- package/skills/productivity/init-beads/SKILL.md +3 -3
- package/skills/productivity/migration/REFERENCE.md +227 -0
- package/skills/productivity/migration/SKILL.md +188 -0
- package/skills/productivity/validate-agent-dir/scripts/{validate-agent-dir.ts → validate-agent-dir.mjs} +49 -28
- package/skills/productivity/write-a-skill/SKILL.md +5 -5
- package/skills/review/maintain-agent-docs/SKILL.md +5 -7
- package/skills/review/refactor-skill/SKILL.md +4 -3
- package/skills/review/review-security/SKILL.md +3 -5
- package/skills/style/style-code/SKILL.md +5 -10
- package/skills/style/style-comms/SKILL.md +5 -7
- package/skills/style/style-documentation/SKILL.md +5 -6
- package/skills/style/style-tests/SKILL.md +5 -7
- package/skills/workflow/git-workflow/SKILL.md +28 -4
- package/skills/workflow/hitl-collab/SKILL.md +4 -6
- package/skills/workflow/plan/SKILL.md +41 -30
- package/skills/workflow/ralph/REFERENCE.md +182 -51
- package/skills/workflow/ralph/SKILL.md +89 -50
- package/skills/workflow/run-pipeline-stage/SKILL.md +72 -71
- package/skills/workflow/run-pipeline-stage/playbooks/code.md +11 -0
- package/skills/workflow/run-pipeline-stage/playbooks/document.md +12 -0
- package/skills/workflow/run-pipeline-stage/playbooks/fix.md +12 -0
- package/skills/workflow/run-pipeline-stage/playbooks/review.md +11 -0
- package/skills/workflow/run-pipeline-stage/playbooks/verify.md +13 -0
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +73 -1
- package/token-map.README.md +9 -4
- package/token-map.json +13 -6
- package/.agents/skills/style-versioning/SKILL.md +0 -172
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.github/workflows/ci.yml +0 -302
- package/.junie/guidelines.md +0 -87
- package/.junie/mcp/mcp.json +0 -10
- package/AGENTS.md +0 -153
- package/agents/ralph/claude/polling.md +0 -31
- package/agents-native/ralph.md +0 -203
- package/docs/claude-hooks.md +0 -144
- package/docs/commit-signing.md +0 -231
- package/docs/ideas/auto-model-selection.md +0 -75
- package/docs/ideas/ban-content-assertion-tests.md +0 -75
- package/docs/ideas/bead-progress-notes-for-session-resumption.md +0 -75
- package/docs/ideas/classify-beads-via-deterministic-script-with-agent-fallback.md +0 -70
- package/docs/ideas/ctx-alias-version-and-update-commands.md +0 -65
- package/docs/ideas/ctx-feedback-command-for-capturing-ideas-from-any-machine.md +0 -67
- package/docs/ideas/dedicated-review-tests-skill.md +0 -71
- package/docs/ideas/deployment-beads-for-post-merge-pipelines.md +0 -36
- package/docs/ideas/design-and-delivery-as-separate-bead-trees/design-and-delivery-as-separate-bead-trees.md +0 -99
- package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +0 -42
- package/docs/ideas/extension-manifests.md +0 -121
- package/docs/ideas/improve-pi-system-prompt.md +0 -43
- package/docs/ideas/map-every-cortex-tool-to-a-real-pi-equivalent.md +0 -95
- package/docs/ideas/model-selector.md +0 -40
- package/docs/ideas/pi-task-memory.md +0 -34
- package/docs/ideas/qa-extension.md +0 -43
- package/docs/ideas/record-idea-implementation-questions.md +0 -68
- package/docs/ideas/record-ideas-as-beads.md +0 -115
- package/docs/ideas/self-flushing-session.md +0 -72
- package/docs/ideas/state-in-beads-not-md.md +0 -69
- package/docs/ideas/subagent-visibility.md +0 -72
- package/docs/ideas/task-session-context.local.md +0 -95
- package/docs/ideas/task-session-context.md +0 -89
- package/docs/ideas/test-data-creation-helpers.md +0 -92
- package/docs/ideas/test-planning-stage-bead-in-feature-planning.md +0 -68
- package/docs/ideas/unified-ci-pipeline.md +0 -28
- package/docs/ideas/unify-the-ticket-and-task-decomposition-workflows.md +0 -71
- package/docs/ideas/worktree-housekeeping-for-closed-beads.md +0 -39
- package/docs/inspirations.md +0 -33
- package/docs/research/disable-model-invocation.md +0 -82
- package/docs/research/gas-city-research.md +0 -256
- package/docs/research/skill-design-research.md +0 -286
- package/docs/research/skill-improvements-analysis.md +0 -117
- package/docs/research/skill-patterns-research.md +0 -24
- package/docs/skills/skill-anatomy.md +0 -200
- package/docs/technical-direction/harness-config.md +0 -177
- package/docs/technical-direction/migration-execution-labels.md +0 -177
- package/docs/user-preferences.md +0 -17
- package/extensions/agent-guardrails/tsconfig.json +0 -12
- package/extensions/agent-modes/tsconfig.json +0 -12
- package/extensions/output-style/tsconfig.json +0 -12
- package/extensions/session-start/tsconfig.json +0 -12
- package/extensions/skill-stats/pnpm-lock.yaml +0 -329
- package/extensions/skill-stats/tsconfig.json +0 -12
- package/extensions/subagent/agents/planner.md +0 -37
- package/extensions/subagent/agents/reviewer.md +0 -35
- package/extensions/subagent/agents/scout.md +0 -50
- package/extensions/subagent/agents/worker.md +0 -24
- package/extensions/subagent/prompts/implement-and-review.md +0 -10
- package/extensions/subagent/prompts/implement.md +0 -10
- package/extensions/subagent/prompts/scout-and-plan.md +0 -9
- package/extensions/subagent/tsconfig.json +0 -12
- package/pi/keybindings.json +0 -3
- package/pi/settings.json +0 -34
- package/pnpm-workspace.yaml +0 -14
- package/skills/planning/create-task/scripts/package.json +0 -14
- package/skills/planning/create-task/scripts/pnpm-lock.yaml +0 -342
- package/skills/planning/create-task/scripts/tsconfig.json +0 -12
- package/skills/planning/record-idea/scripts/new-idea.sh +0 -89
- package/skills/productivity/validate-agent-dir/scripts/package.json +0 -15
- package/skills/productivity/validate-agent-dir/scripts/pnpm-lock.yaml +0 -329
- package/skills/productivity/validate-agent-dir/scripts/pnpm-workspace.yaml +0 -2
- package/skills/productivity/validate-agent-dir/scripts/tsconfig.json +0 -19
- package/skills/productivity/validate-agent-dir/scripts/validate-agent-dir.test.ts +0 -317
- package/skills/workflow/run-pipeline-stage/REFERENCE.md +0 -22
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +0 -682
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +0 -588
- package/skills/workflow/run-pipeline-stage/scripts/package.json +0 -15
- package/skills/workflow/run-pipeline-stage/scripts/pnpm-lock.yaml +0 -342
- package/skills/workflow/run-pipeline-stage/scripts/pnpm-workspace.yaml +0 -2
- package/skills/workflow/run-pipeline-stage/scripts/tsconfig.json +0 -12
- package/test/classify-bead.test.mjs +0 -277
- package/test/claude-installer.test.mjs +0 -308
- package/test/cli.test.mjs +0 -314
- package/test/create-chores.test.mjs +0 -85
- package/test/fake-bd.test.mjs +0 -72
- package/test/fs.test.mjs +0 -76
- package/test/helpers/fake-bd.mjs +0 -87
- package/test/helpers/fake-claude.mjs +0 -152
- package/test/helpers/fake-pi.mjs +0 -132
- package/test/installer.test.mjs +0 -806
- package/test/notify-hook.test.mjs +0 -63
- package/test/pending-gates.test.mjs +0 -147
- package/test/pi-installer.test.mjs +0 -785
- package/test/pi-register.test.mjs +0 -342
- package/test/pi-sweep.test.mjs +0 -619
- package/test/release-gates.test.mjs +0 -190
- package/test/skill-frontmatter.test.mjs +0 -137
- package/test/verify-worker-tree.test.mjs +0 -120
|
@@ -16,9 +16,21 @@ const GITHUB_ACTIONS_BOT = "github-actions[bot]";
|
|
|
16
16
|
const CHANGESET_PATH = /^\.changeset\/[^/]+\.md$/;
|
|
17
17
|
const CHANGESET_EXEMPT_PATHS = new Set([".changeset/README.md"]);
|
|
18
18
|
|
|
19
|
+
/** @typedef {{ status: string, path: string }} NameStatusEntry */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} ChangesetGateInput
|
|
23
|
+
* @property {NameStatusEntry[]} changes
|
|
24
|
+
* @property {string[]} labels
|
|
25
|
+
* @property {string} headRef
|
|
26
|
+
* @property {string} actor
|
|
27
|
+
*/
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* Parse `git diff --name-status` output into `{ status, path }` entries, where
|
|
21
31
|
* `status` is the single-letter change type and `path` is the new path.
|
|
32
|
+
* @param {string} output
|
|
33
|
+
* @returns {NameStatusEntry[]}
|
|
22
34
|
*/
|
|
23
35
|
export function parseNameStatus(output) {
|
|
24
36
|
return output
|
|
@@ -26,7 +38,7 @@ export function parseNameStatus(output) {
|
|
|
26
38
|
.filter((line) => line.trim() !== "")
|
|
27
39
|
.map((line) => {
|
|
28
40
|
const [status, ...paths] = line.split("\t");
|
|
29
|
-
return { status: status.charAt(0), path: paths.at(-1) };
|
|
41
|
+
return { status: status.charAt(0), path: /** @type {string} */ (paths.at(-1)) };
|
|
30
42
|
});
|
|
31
43
|
}
|
|
32
44
|
|
|
@@ -34,6 +46,8 @@ export function parseNameStatus(output) {
|
|
|
34
46
|
* Decide whether a pull request may skip adding a changeset. Exempt only the
|
|
35
47
|
* Version Packages PR and PRs explicitly labelled `no-publish`; every other PR
|
|
36
48
|
* must add its own changeset.
|
|
49
|
+
* @param {ChangesetGateInput} input
|
|
50
|
+
* @returns {{ ok: boolean, reason: string }}
|
|
37
51
|
*/
|
|
38
52
|
export function evaluateChangesetGate({ changes, labels, headRef, actor }) {
|
|
39
53
|
if (headRef.startsWith(VERSION_PACKAGES_HEAD_REF) || actor === GITHUB_ACTIONS_BOT) {
|
|
@@ -51,11 +65,17 @@ export function evaluateChangesetGate({ changes, labels, headRef, actor }) {
|
|
|
51
65
|
/**
|
|
52
66
|
* Resolve the command changesets/action should exec for publishing, based on
|
|
53
67
|
* the labels of the PR merged for the pushed commit.
|
|
68
|
+
* @param {string[]} labels
|
|
69
|
+
* @returns {string}
|
|
54
70
|
*/
|
|
55
71
|
export function resolvePublishCommand(labels) {
|
|
56
72
|
return labels.includes(NO_PUBLISH_LABEL) ? NO_PUBLISH_COMMAND : PUBLISH_COMMAND;
|
|
57
73
|
}
|
|
58
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} path
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
59
79
|
function isChangeset(path) {
|
|
60
80
|
return CHANGESET_PATH.test(path) && !CHANGESET_EXEMPT_PATHS.has(path);
|
|
61
81
|
}
|
|
@@ -33,6 +33,8 @@ const SKIPPED_DIRS = new Set([".git", "node_modules"]);
|
|
|
33
33
|
* Return the YAML text between the leading `---` delimiters, or null when the
|
|
34
34
|
* document has no frontmatter block. Mirrors PI's extraction step, including
|
|
35
35
|
* newline normalisation and BOM stripping.
|
|
36
|
+
* @param {string} content
|
|
37
|
+
* @returns {string | null}
|
|
36
38
|
*/
|
|
37
39
|
function extractYaml(content) {
|
|
38
40
|
const normalized = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -44,7 +46,13 @@ function extractYaml(content) {
|
|
|
44
46
|
return normalized.slice(4, endIndex);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* True when `value` is a string with at least one non-whitespace character.
|
|
51
|
+
* `yaml`'s `parse` is typed `any`, so the frontmatter values it yields are
|
|
52
|
+
* taken as `unknown` here and narrowed by this typeof guard.
|
|
53
|
+
* @param {unknown} value
|
|
54
|
+
* @returns {value is string}
|
|
55
|
+
*/
|
|
48
56
|
function isNonEmptyString(value) {
|
|
49
57
|
return typeof value === "string" && value.trim() !== "";
|
|
50
58
|
}
|
|
@@ -61,6 +69,7 @@ export function validateSkillFrontmatter(content) {
|
|
|
61
69
|
return ["frontmatter block is missing (expected '---' delimiters)"];
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
/** @type {unknown} */
|
|
64
73
|
let frontmatter;
|
|
65
74
|
try {
|
|
66
75
|
frontmatter = parse(yamlText);
|
|
@@ -73,23 +82,31 @@ export function validateSkillFrontmatter(content) {
|
|
|
73
82
|
return ["frontmatter is not a YAML mapping of name/description fields"];
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
// The guard above establishes a non-null, non-array object, so the YAML
|
|
86
|
+
// mapping's fields are readable as unknown values.
|
|
87
|
+
const fields = /** @type {Record<string, unknown>} */ (frontmatter);
|
|
76
88
|
const errors = [];
|
|
77
|
-
if (!isNonEmptyString(
|
|
89
|
+
if (!isNonEmptyString(fields.name)) {
|
|
78
90
|
errors.push("frontmatter is missing a non-empty name");
|
|
79
91
|
}
|
|
80
|
-
if (!isNonEmptyString(
|
|
92
|
+
if (!isNonEmptyString(fields.description)) {
|
|
81
93
|
errors.push("frontmatter is missing a non-empty description");
|
|
82
|
-
} else if (
|
|
94
|
+
} else if (fields.description.length > MAX_DESCRIPTION_LENGTH) {
|
|
83
95
|
errors.push(
|
|
84
|
-
`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${
|
|
96
|
+
`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${fields.description.length})`,
|
|
85
97
|
);
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
return errors;
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Recursively collect every SKILL.md below `dir`, skipping vendored trees.
|
|
105
|
+
* @param {string} dir
|
|
106
|
+
* @returns {Promise<string[]>}
|
|
107
|
+
*/
|
|
92
108
|
async function findSkillFiles(dir) {
|
|
109
|
+
/** @type {string[]} */
|
|
93
110
|
const found = [];
|
|
94
111
|
let entries;
|
|
95
112
|
try {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Flat-layout link rewriting for installed skills.
|
|
2
|
+
//
|
|
3
|
+
// Skills are authored in a grouped tree (skills/<group>/<name>/), so a handoff to
|
|
4
|
+
// a neighbouring skill is written against that tree — ../../style/style-code/SKILL.md.
|
|
5
|
+
// The pi installer preserves the grouping, so those paths resolve as written. The
|
|
6
|
+
// Claude Code plugin cannot: its scan expects skills/<name>/SKILL.md directly under
|
|
7
|
+
// skills/, so bin/installers/claude.mjs copies every skill flat and the authored path
|
|
8
|
+
// loses its group segment. Rewriting the link at copy time keeps one authored source
|
|
9
|
+
// that is correct in the repo and correct in both install layouts.
|
|
10
|
+
//
|
|
11
|
+
// Zero dependencies so it runs on the CI Node (20) and local Node alike.
|
|
12
|
+
//
|
|
13
|
+
// Known limits. The rewrite keys off the first path segment that matches a skill name,
|
|
14
|
+
// so it cannot tell a skill directory from a same-named directory elsewhere: a target
|
|
15
|
+
// like ../../../docs/plan/x.md (plan is a skill name) becomes ../plan/x.md, pointing at
|
|
16
|
+
// a file that does not exist in the flattened install. An angle-bracket target such as
|
|
17
|
+
// <plan> loses its brackets and gains a ../ for the same reason. Neither shape exists in
|
|
18
|
+
// the corpus today — links only ever enter skills/ trees with plain relative targets —
|
|
19
|
+
// so this is a constraint on new links, not a live bug. A non-md file that links back
|
|
20
|
+
// into a skill tree has the same caveat: only markdown under a skill directory is
|
|
21
|
+
// transformed.
|
|
22
|
+
|
|
23
|
+
const LINK_RE = /\]\(([^)\s]+)\)/g;
|
|
24
|
+
const NON_RELATIVE_RE = /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Rewrite relative markdown links that traverse a skill directory into flat sibling
|
|
28
|
+
* references, so they resolve after the grouped tree is flattened.
|
|
29
|
+
*
|
|
30
|
+
* `../../style/style-code/SKILL.md` → `../style-code/SKILL.md`. Links that do not
|
|
31
|
+
* traverse a skill directory (`./EXAMPLES.md`, `../tdd/tests.md`) are left alone, as
|
|
32
|
+
* are schemes, absolute paths, and anchors.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} content Markdown file content
|
|
35
|
+
* @param {Set<string> | string[]} skillNames Every skill name in the install
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
export function flattenSkillLinks(content, skillNames) {
|
|
39
|
+
const names = skillNames instanceof Set ? skillNames : new Set(skillNames);
|
|
40
|
+
/** @param {string} match @param {string} target @returns {string} */
|
|
41
|
+
return content.replace(LINK_RE, (match, target) => {
|
|
42
|
+
if (NON_RELATIVE_RE.test(target)) return match;
|
|
43
|
+
const segments = target.split("/");
|
|
44
|
+
/** @param {string} segment @returns {boolean} */
|
|
45
|
+
const isSkill = (segment) => names.has(segment);
|
|
46
|
+
const skillAt = segments.findIndex(isSkill);
|
|
47
|
+
// First path segment: a same-directory link, already correct when flattened.
|
|
48
|
+
if (skillAt <= 0) return match;
|
|
49
|
+
const rest = segments.slice(skillAt + 1);
|
|
50
|
+
return `](../${[segments[skillAt], ...rest].join("/")})`;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -26,11 +26,20 @@ import { fileURLToPath } from "node:url";
|
|
|
26
26
|
const execFileAsync = promisify(execFile);
|
|
27
27
|
const SELF = fileURLToPath(import.meta.url);
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} worktree
|
|
31
|
+
* @param {string[]} args
|
|
32
|
+
* @returns {Promise<string>}
|
|
33
|
+
*/
|
|
29
34
|
async function git(worktree, args) {
|
|
30
35
|
const { stdout } = await execFileAsync("git", args, { cwd: worktree });
|
|
31
36
|
return stdout.trim();
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} porcelain
|
|
41
|
+
* @returns {number}
|
|
42
|
+
*/
|
|
34
43
|
function countPaths(porcelain) {
|
|
35
44
|
return porcelain.split("\n").filter((line) => line.trim() !== "").length;
|
|
36
45
|
}
|
|
@@ -39,6 +48,8 @@ function countPaths(porcelain) {
|
|
|
39
48
|
* Check that a worker delivered: clean worktree and at least one commit ahead
|
|
40
49
|
* of `base`. Returns the reasons it failed, so the caller can surface them
|
|
41
50
|
* instead of guessing why a stage did not advance.
|
|
51
|
+
* @param {{ worktree: string, base: string }} options
|
|
52
|
+
* @returns {Promise<{ ok: boolean, reasons: string[], commitsAhead: number }>}
|
|
42
53
|
*/
|
|
43
54
|
export async function verifyWorkerTree({ worktree, base }) {
|
|
44
55
|
const reasons = [];
|
|
@@ -58,7 +69,12 @@ export async function verifyWorkerTree({ worktree, base }) {
|
|
|
58
69
|
|
|
59
70
|
// ─── CLI ─────────────────────────────────────────────────────────────────────
|
|
60
71
|
|
|
72
|
+
/**
|
|
73
|
+
* @param {string[]} argv
|
|
74
|
+
* @returns {Record<string, string | true>}
|
|
75
|
+
*/
|
|
61
76
|
function parseFlags(argv) {
|
|
77
|
+
/** @type {Record<string, string | true>} */
|
|
62
78
|
const flags = {};
|
|
63
79
|
for (let i = 0; i < argv.length; i++) {
|
|
64
80
|
const arg = argv[i];
|
|
@@ -75,6 +91,11 @@ function parseFlags(argv) {
|
|
|
75
91
|
return flags;
|
|
76
92
|
}
|
|
77
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @param {Record<string, string | true>} flags
|
|
96
|
+
* @param {string} name
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
78
99
|
function required(flags, name) {
|
|
79
100
|
const value = flags[name];
|
|
80
101
|
if (typeof value !== "string" || value === "") {
|
|
@@ -84,6 +105,10 @@ function required(flags, name) {
|
|
|
84
105
|
return value;
|
|
85
106
|
}
|
|
86
107
|
|
|
108
|
+
/**
|
|
109
|
+
* @param {string[]} argv
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
87
112
|
async function main(argv) {
|
|
88
113
|
const flags = parseFlags(argv);
|
|
89
114
|
const verdict = await verifyWorkerTree({
|
|
@@ -93,10 +93,10 @@ description: "Generate multiple radically different interface designs for a modu
|
|
|
93
93
|
- [ ] Phase 4 complete: comparison shared and divergence highlighted
|
|
94
94
|
- [ ] Phase 5 complete: candidate synthesized with user input
|
|
95
95
|
|
|
96
|
-
##
|
|
96
|
+
## See Also
|
|
97
97
|
|
|
98
|
-
-
|
|
99
|
-
-
|
|
98
|
+
- [improve-codebase-architecture](../improve-codebase-architecture/SKILL.md) — runs this skill for its multi-design step; load it when the designs feed a deepening decision.
|
|
99
|
+
- [grill-with-docs](../grill-with-docs/SKILL.md) — load when the module's domain terminology is unsettled, so design names match the ubiquitous language.
|
|
100
100
|
|
|
101
101
|
## Examples
|
|
102
102
|
|
|
@@ -53,12 +53,10 @@ description: "Grilling session that challenges your plan against the existing do
|
|
|
53
53
|
- **`CONTEXT.md` is a glossary and nothing else** — the vocabulary stays stable while the design churns around it.
|
|
54
54
|
- **ADRs are rare** — the three criteria keep the ADR log a history of decisions, not a diary.
|
|
55
55
|
|
|
56
|
-
##
|
|
56
|
+
## See Also
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
| A design interview without touching project docs | `grill-me` |
|
|
61
|
-
| A justified recommendation on architecture or technology | `technical-direction` |
|
|
58
|
+
- [grill-me](../../productivity/grill-me/SKILL.md) — the same interview without the doc updates, when no CONTEXT.md or ADR work is wanted.
|
|
59
|
+
- [technical-direction](../technical-direction/SKILL.md) — continues into a justified recommendation once the terminology has settled.
|
|
62
60
|
|
|
63
61
|
## Examples
|
|
64
62
|
|
|
@@ -76,11 +76,12 @@ A **deep module** (John Ousterhout, "A Philosophy of Software Design") has a sma
|
|
|
76
76
|
- [ ] Phase 3 complete: at least two radically different designs presented and compared, with a recommendation given
|
|
77
77
|
- [ ] Phase 4 complete: refactor bead created via `create-task` and the bead ID reported
|
|
78
78
|
|
|
79
|
-
##
|
|
79
|
+
## See Also
|
|
80
80
|
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
81
|
+
- [design-an-interface](../design-an-interface/SKILL.md) — step 5 hands the deep-module redesign to its parallel design flow.
|
|
82
|
+
- [create-task](../../planning/create-task/SKILL.md) — step 7 files the refactor as a bead; never call `bd create` directly.
|
|
83
|
+
- [grill-with-docs](../grill-with-docs/SKILL.md) — load when the deepened module's terminology is unsettled.
|
|
84
|
+
- [style-code](../../style/style-code/SKILL.md) — load when the deepened module needs the naming and boundary conventions it will ship with.
|
|
84
85
|
|
|
85
86
|
## Examples
|
|
86
87
|
|
|
@@ -91,15 +91,13 @@ After all tests pass, run `style-code` to guide the refactoring shape:
|
|
|
91
91
|
| "This is too simple for TDD" | The simplest changes often have the most hidden assumptions. TDD surfaces them early. |
|
|
92
92
|
| "I already know what the implementation looks like" | Then write the test first to prove it. If the test forces a different interface, the implementation was wrong. |
|
|
93
93
|
|
|
94
|
-
##
|
|
94
|
+
## See Also
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| Code-level style and conventions | `style-code` |
|
|
100
|
-
| Domain terminology for test names and module boundaries | `grill-with-docs` |
|
|
96
|
+
- [style-tests](../../style/style-tests/SKILL.md) — load before writing the first test; it owns assertion strategy, mock discipline, and test-data construction.
|
|
97
|
+
- [style-code](../../style/style-code/SKILL.md) — load at the refactor step for naming and module boundaries.
|
|
98
|
+
- [grill-with-docs](../grill-with-docs/SKILL.md) — load when domain vocabulary is unsettled, so test names and seams use the project's own language.
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
Bundled reference docs: `tests.md`, `mocking.md`, `deep-modules.md`, `interface-design.md`, `refactoring.md`.
|
|
103
101
|
|
|
104
102
|
## Examples
|
|
105
103
|
|
|
@@ -54,12 +54,12 @@ Act as a pragmatic, challenging staff engineer: constructive, direct, and eviden
|
|
|
54
54
|
- **Challenge is the deliverable.** The value of a direction session is the assumptions it surfaces and kills. Optimising for agreement produces a recommendation nobody tested.
|
|
55
55
|
- **Every recommendation carries its tradeoffs.** A choice without documented tradeoffs, revisit triggers, and validation signals cannot be revisited intelligently later.
|
|
56
56
|
|
|
57
|
-
##
|
|
57
|
+
## See Also
|
|
58
58
|
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
59
|
+
- [write-a-prd](../../planning/write-a-prd/SKILL.md) — load when the decision needs a requirements brief that does not exist yet.
|
|
60
|
+
- [grill-with-docs](../grill-with-docs/SKILL.md) — load when the contested point is vocabulary rather than technology.
|
|
61
|
+
- [design-an-interface](../design-an-interface/SKILL.md) — load when the decision is interface shape and option generation needs structure.
|
|
62
|
+
- [style-documentation](../../style/style-documentation/SKILL.md) — load to decide whether the decision memo gets written at all.
|
|
63
63
|
|
|
64
64
|
## Examples
|
|
65
65
|
|
|
@@ -7,13 +7,13 @@ truth for the deterministic tiers; this document mirrors it for verification.
|
|
|
7
7
|
|
|
8
8
|
| Tier | `source` | Evidence | Applies the label? |
|
|
9
9
|
|---|---|---|---|
|
|
10
|
-
| 1 | `label` | An `
|
|
10
|
+
| 1 | `label` | An `execution:afk` or `execution:hitl` label is already present | no — it is already there |
|
|
11
11
|
| 2 | `type-field` | The legacy `## Type` section in the bead body declares `AFK`/`HITL` (block or inline form, case-insensitive) | yes |
|
|
12
12
|
| 3 | `heuristic` | A conservative natural-language signal (below) | yes |
|
|
13
13
|
| 4 | *(none)* | Nothing deterministic matched → `escalate: true`; a small/cheap model applies the rubric | after the model decides |
|
|
14
14
|
|
|
15
|
-
When both `
|
|
16
|
-
pre-existing skill wording, which checks `
|
|
15
|
+
When both `execution` labels are present, AFK wins — matching the
|
|
16
|
+
pre-existing skill wording, which checks `execution:afk` first.
|
|
17
17
|
|
|
18
18
|
The classifier runs `bd show <id> --json` and `bd tag <id> <label>`; it never
|
|
19
19
|
re-reads prose from the rendered `bd show` output.
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: classify-bead
|
|
3
|
-
description: Classify a bead as AFK or HITL and apply
|
|
3
|
+
description: Classify a bead as AFK or HITL and apply its `execution` label. Use when a bead is missing its execution label, when picking up a bead to work on, or when any skill creates a new bead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Classify Bead
|
|
7
7
|
|
|
8
|
-
Determine whether a bead requires human action (HITL) or can be completed autonomously by an agent (AFK), then persist the result as an `
|
|
8
|
+
Determine whether a bead requires human action (HITL) or can be completed autonomously by an agent (AFK), then persist the result as an `execution` label (`execution:afk` or `execution:hitl`). A deterministic classifier settles the common cases; only genuine rubric judgement reaches a model.
|
|
9
9
|
|
|
10
10
|
## When to use
|
|
11
11
|
|
|
12
|
-
- A bead is missing its `
|
|
12
|
+
- A bead is missing its `execution` label and you are about to pick it up.
|
|
13
13
|
- A skill has just created a new bead and needs it classified before claiming or scheduling.
|
|
14
14
|
- You are unsure of a bead's classification and want to resolve it deterministically.
|
|
15
15
|
|
|
16
16
|
## When NOT to use
|
|
17
17
|
|
|
18
|
-
- The bead already carries an `
|
|
18
|
+
- The bead already carries an `execution` label — check `bd label list <id>` before invoking, and never re-classify a labelled bead.
|
|
19
19
|
- You are creating a new task — `create-task` classifies internally; do not spawn this skill separately.
|
|
20
20
|
- The work item is not a bead (e.g. a Jira ticket or plain code task) — use `write-a-ticket` or start the work directly.
|
|
21
21
|
|
|
22
22
|
## Invocation
|
|
23
23
|
|
|
24
|
-
Run the deterministic classifier first. It resolves the existing label, the legacy `## Type` field, and explicit heuristic signals without a model call:
|
|
24
|
+
Run the deterministic classifier first. It resolves the existing `execution` label, the legacy `## Type` field, and explicit heuristic signals without a model call:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
# Run from the TARGET project's directory — bd resolves its beads DB from cwd.
|
|
@@ -34,11 +34,11 @@ node <skill-scripts>/classify-bead.mjs <bead-id>
|
|
|
34
34
|
|
|
35
35
|
`<skill-scripts>` is the absolute path to this skill's own `scripts/` directory, resolved from wherever this skill was loaded — it varies by harness and install location, so never hardcode one harness's path. Only run inline when no task-spawning capability exists in the current environment.
|
|
36
36
|
|
|
37
|
-
**Callers should run the classifier themselves before spawning this subagent.** If `
|
|
37
|
+
**Callers should run the classifier themselves before spawning this subagent.** If an `execution` label is already present, or the classifier resolves the bead, there is no subagent call to make.
|
|
38
38
|
|
|
39
39
|
## Workflow
|
|
40
40
|
|
|
41
|
-
1. **Check for an existing label.** `bd label list <id>` — `
|
|
41
|
+
1. **Check for an existing label.** `bd label list <id>` — `execution:afk` present → **AFK**; `execution:hitl` present → **HITL**. Stop.
|
|
42
42
|
|
|
43
43
|
2. **Run the deterministic classifier.** `node <skill-scripts>/classify-bead.mjs <id>`. A resolved classification is already tagged; stop and report it.
|
|
44
44
|
|
|
@@ -51,7 +51,7 @@ node <skill-scripts>/classify-bead.mjs <bead-id>
|
|
|
51
51
|
|
|
52
52
|
**AFK** — otherwise: the agent can implement, verify, and complete the task autonomously, and all acceptance criteria are machine-checkable.
|
|
53
53
|
|
|
54
|
-
4. **Apply the label.** `bd tag <id>
|
|
54
|
+
4. **Apply the label.** `bd tag <id> execution:afk` (or `execution:hitl`). The classifier does this for the deterministic tiers; do it yourself only after step 3.
|
|
55
55
|
|
|
56
56
|
5. **Return the classification.** Report `AFK` or `HITL` so the calling skill or agent can act on it.
|
|
57
57
|
|
|
@@ -77,11 +77,16 @@ Tier precedence (label → `## Type` → heuristics → rubric), the heuristic s
|
|
|
77
77
|
- Classification gates automation against human attention: AFK beads feed ralph's autonomous pipeline, HITL beads route to a person. Prefer AFK because human time is the scarce resource, and later review gates still catch agent mistakes.
|
|
78
78
|
- The rubric is deterministic, so most classifications are a lookup. The executable encodes that lookup once, and the model is reserved for beads whose evidence is genuinely ambiguous.
|
|
79
79
|
|
|
80
|
+
## See Also
|
|
81
|
+
|
|
82
|
+
- [create-task](../create-task/SKILL.md) — calls this classifier for every bead it creates; the two must agree on label vocabulary.
|
|
83
|
+
- [ralph](../../workflow/ralph/SKILL.md) — reads `bd ready -l execution:afk`; a mislabelled bead is invisible to the pipeline.
|
|
84
|
+
|
|
80
85
|
## Verification checklist
|
|
81
86
|
|
|
82
87
|
- [ ] `bd label list <id>` was checked before any other step.
|
|
83
88
|
- [ ] The deterministic classifier ran before any rubric subagent.
|
|
84
89
|
- [ ] A rubric subagent ran only when the classifier reported `escalate: true`.
|
|
85
90
|
- [ ] The full bead body was read before classifying from first principles.
|
|
86
|
-
- [ ] `bd tag <id>
|
|
91
|
+
- [ ] `bd tag <id> execution:<afk|hitl>` is present afterwards.
|
|
87
92
|
- [ ] The calling skill or agent received the classification in the report.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
// Deterministic bead classifier — resolves an AFK/HITL classification from
|
|
2
3
|
// evidence already present on a bead before any model judgement is spent.
|
|
3
4
|
//
|
|
4
5
|
// Tier order (see skills/planning/classify-bead/SKILL.md):
|
|
5
|
-
// 1.
|
|
6
|
+
// 1. execution label — an existing decision is authoritative
|
|
6
7
|
// 2. legacy ## Type field — back-compat with pre-label beads
|
|
7
8
|
// 3. conservative heuristics — explicit natural-language signals only
|
|
8
9
|
// 4. escalate — hand to the rubric (small/cheap model)
|
|
@@ -24,8 +25,8 @@ import { pathToFileURL } from 'node:url';
|
|
|
24
25
|
* @property {boolean} escalate True when model judgement is required (tier 4).
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
|
-
const AFK_LABEL = '
|
|
28
|
-
const HITL_LABEL = '
|
|
28
|
+
const AFK_LABEL = 'execution:afk';
|
|
29
|
+
const HITL_LABEL = 'execution:hitl';
|
|
29
30
|
|
|
30
31
|
// Heuristic signals are deliberately conservative: they only fire on explicit
|
|
31
32
|
// natural-language phrasing that maps directly onto the rubric's criteria.
|
|
@@ -94,7 +95,7 @@ function extractTypeField(description) {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
|
-
* Resolve a bead's
|
|
98
|
+
* Resolve a bead's execution class from the evidence it already carries,
|
|
98
99
|
* without spending a model call.
|
|
99
100
|
*
|
|
100
101
|
* @param {{ labels?: string[], description?: string }} bead
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Create Task — Reference
|
|
2
2
|
|
|
3
|
-
Detail behind `skills/planning/create-task/SKILL.md`. The script (`scripts/create-chores.
|
|
3
|
+
Detail behind `skills/planning/create-task/SKILL.md`. The script (`scripts/create-chores.mjs`) is the source of truth for this contract; this document mirrors it for verification.
|
|
4
4
|
|
|
5
5
|
## Script invocation
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
|
|
8
|
+
node <skill-scripts>/create-chores.mjs --parent <bead-id> [--priority <0-4>]
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
- Reads `<skill>/pipeline.json`; creates each stage chore via `bd create` and wires dependencies via `bd dep add`.
|
|
12
12
|
- Prints a JSON object mapping each stage ID to its chore bead ID, plus `featurePrReview` for the PR gate.
|
|
13
|
+
- Expands a bead that already exists — it never creates a parent. It is also not idempotent: running it against a bead that already has stage chores creates a second chain, so callers check for existing `stage:*` children first (ralph's expansion guard).
|
|
13
14
|
|
|
14
15
|
## Stage chore beads
|
|
15
16
|
|
|
@@ -24,10 +25,58 @@ One per stage in `pipeline.json` (currently `code`, `verify`, `review`, `documen
|
|
|
24
25
|
|
|
25
26
|
- **Type**: `task`
|
|
26
27
|
- **Title**: `[<parent-id>] PR Review and Merge`
|
|
27
|
-
- **Labels**: `
|
|
28
|
+
- **Labels**: `execution:hitl`, `lifecycle:feature-pr` — exactly one `execution` label. The gate declares its own labels, so the script passes `--no-inherit-labels` and it never picks up the parent's `execution:afk`. Stage chores do **not** opt out; they keep inheriting `execution:afk` from the parent.
|
|
29
|
+
- **Parent contract**: the parent must carry `execution:afk`. Stage chores inherit the parent's labels, so a parent labelled `execution:hitl` (or unlabelled) produces a chore tree that no executor routes. The script does not classify — it only checks that it was given a parent ID.
|
|
28
30
|
- **Dependencies**: `parent-child` to the parent bead; `blocks` on the final document stage chore (`document`, falling back to the last stage in `pipeline.json`).
|
|
29
31
|
- **Purpose**: human review of the agent PR (agent branch → feature branch) before the feature closes.
|
|
30
32
|
|
|
33
|
+
## Hierarchy
|
|
34
|
+
|
|
35
|
+
Ownership is `--parent` at create time; ordering is `bd dep add`. The full shape for a
|
|
36
|
+
workstream, from container down to stage chore:
|
|
37
|
+
|
|
38
|
+
| Level | Type | Execution | Created by | Ownership |
|
|
39
|
+
|---|---|---|---|---|
|
|
40
|
+
| Feature | `feature` | `execution:hitl` | `create-task` step 5 | — |
|
|
41
|
+
| Planning bead ("Plan: <feature title>") | `task` | `execution:hitl` | `create-task` step 6 | `--parent <feature>` |
|
|
42
|
+
| Impl task (one per deliverable) | `task` | `execution:afk` | `create-task` step 6 | `--parent <feature>` |
|
|
43
|
+
| Stage chore | `chore` | inherited `execution:afk` | `create-chores.mjs` | `--parent <impl-task>` |
|
|
44
|
+
| PR gate | `task` | `execution:hitl` | `create-chores.mjs` | `--parent <impl-task>`, `--no-inherit-labels` |
|
|
45
|
+
|
|
46
|
+
Stage chores come from running `create-chores.mjs` once per impl task (`--parent <impl-task>`),
|
|
47
|
+
never against the feature itself — its parent must carry `execution:afk`.
|
|
48
|
+
|
|
49
|
+
Every impl task depends on the planning bead (`bd dep add <impl-task> <planning-bead>`), so
|
|
50
|
+
`bd ready` withholds them until planning closes. Nothing aggregates a child's execution class
|
|
51
|
+
onto its parent: the container is human-owned because a person decides when it is done, and
|
|
52
|
+
each child states the class of its own work.
|
|
53
|
+
|
|
54
|
+
### Planning bead body
|
|
55
|
+
|
|
56
|
+
Cold-start ready — another person picks it up with no chat history:
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
## What and why
|
|
60
|
+
|
|
61
|
+
The deliverable this feature produces and the problem it solves.
|
|
62
|
+
|
|
63
|
+
## Decisions already made
|
|
64
|
+
|
|
65
|
+
- Decision — rationale
|
|
66
|
+
|
|
67
|
+
## Open questions / risks
|
|
68
|
+
|
|
69
|
+
- Question — who answers it
|
|
70
|
+
|
|
71
|
+
## References
|
|
72
|
+
|
|
73
|
+
- Bead IDs, files, docs consulted
|
|
74
|
+
|
|
75
|
+
## Acceptance Criteria
|
|
76
|
+
|
|
77
|
+
- [ ] The user confirms planning is sufficient for this feature.
|
|
78
|
+
```
|
|
79
|
+
|
|
31
80
|
## Current pipeline stages
|
|
32
81
|
|
|
33
82
|
| id | title | dependsOn |
|