@jaybeeuu/agent-cortex 2.0.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 +437 -0
- package/README.md +46 -15
- package/agents/README.md +22 -10
- package/agents/plan/agent.md +2 -2
- package/agents/ralph/agent.md +128 -91
- 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 +262 -77
- 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/extensions/output-style/README.md +58 -0
- package/extensions/output-style/index.ts +23 -0
- package/extensions/output-style/output-style.md +43 -0
- package/extensions/output-style/output-style.test.ts +50 -0
- package/extensions/output-style/output-style.ts +32 -0
- package/extensions/output-style/package.json +19 -0
- package/hooks/claude/scripts/notify.mjs +8 -1
- package/lib/cli.mjs +91 -2
- package/package.json +29 -4
- package/scripts/check-changeset.mjs +4 -0
- package/scripts/check-no-sync.mjs +5 -1
- package/scripts/check-skill-frontmatter.mjs +21 -0
- 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 +152 -0
- 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 +209 -55
- package/skills/workflow/ralph/SKILL.md +89 -50
- package/skills/workflow/ralph/scripts/pending-gates.mjs +134 -0
- 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 -151
- package/agents/ralph/claude/polling.md +0 -31
- package/agents-native/ralph.md +0 -193
- package/docs/claude-hooks.md +0 -128
- 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 -37
- 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 -197
- 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/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 -664
- package/test/notify-hook.test.mjs +0 -63
- 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/verify-worker-tree.test.mjs +0 -120
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Output Style
|
|
2
|
+
|
|
3
|
+
Appends the always-on reply shape to the PI system prompt once per user prompt.
|
|
4
|
+
|
|
5
|
+
This is the second slice of `docs/ideas/improve-pi-system-prompt.md`: the first
|
|
6
|
+
(`agent-guardrails`) stops wasted work; this one stops the answer getting buried in
|
|
7
|
+
it. The extension injects a stable block via the `before_agent_start` hook — which
|
|
8
|
+
fires once per user prompt, before the agent loop — so the rules apply to every
|
|
9
|
+
turn of the run without rewriting Pi's default prompt.
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
|
|
13
|
+
- **Lead with the action** — the first line is the answer, command, path, or next
|
|
14
|
+
action; multi-step work is numbered; the reply ends with one next step.
|
|
15
|
+
- **Keep the prose short** — no preamble, no recap, no closing offer; five items
|
|
16
|
+
per list at most; one topic per reply.
|
|
17
|
+
- **Write Simplified Technical English (ASD-STE100)** — active voice, 20-word
|
|
18
|
+
sentences for instructions and 25-word sentences for description, verb over
|
|
19
|
+
noun, one word one meaning, literal words, no marketing adjectives, articles
|
|
20
|
+
kept.
|
|
21
|
+
- **Restate the context** — never cite a bead, ticket, phase, or stage by ID alone;
|
|
22
|
+
say what it is in plain words first, and restate state after a gap or topic
|
|
23
|
+
switch.
|
|
24
|
+
|
|
25
|
+
The rules shape the reply, never the work. When a rule would delete the answer,
|
|
26
|
+
the task wins.
|
|
27
|
+
|
|
28
|
+
## Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
output-style/
|
|
32
|
+
├── README.md # This file
|
|
33
|
+
├── index.ts # Extension entry point — appends on before_agent_start
|
|
34
|
+
├── output-style.ts # Loads the block + appendOutputStyle composition
|
|
35
|
+
├── output-style.md # The block text (single source, also shipped to Claude)
|
|
36
|
+
├── output-style.test.ts # Composition tests
|
|
37
|
+
├── package.json
|
|
38
|
+
└── tsconfig.json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Testing
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd extensions/output-style
|
|
45
|
+
node --test --import tsx/esm output-style.test.ts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Design notes
|
|
49
|
+
|
|
50
|
+
- The block is a stable constant appended after any chained system-prompt changes,
|
|
51
|
+
so the prompt-cache prefix is preserved.
|
|
52
|
+
- `appendOutputStyle` is idempotent — a prompt that already carries the marker is
|
|
53
|
+
returned unchanged, so a duplicate extension load cannot double the rules.
|
|
54
|
+
- Separate from `agent-guardrails` because it is a different failure: guardrails
|
|
55
|
+
govern *whether* work continues, this governs *how the result is reported*.
|
|
56
|
+
- The prose lives in `output-style.md`, not in the TypeScript, because there is a
|
|
57
|
+
second consumer: `agent-cortex install claude` wraps that file in the managed
|
|
58
|
+
`~/.claude/CLAUDE.md` section. One file, two harnesses, no drift.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output Style Extension
|
|
3
|
+
*
|
|
4
|
+
* Appends the always-on reply shape to the PI system prompt on every turn: lead
|
|
5
|
+
* with the action, write in Simplified Technical English (ASD-STE100), restate the
|
|
6
|
+
* context the reader cannot remember (the second slice of
|
|
7
|
+
* docs/ideas/improve-pi-system-prompt.md).
|
|
8
|
+
*
|
|
9
|
+
* The block is stable text appended after any chained system-prompt changes, so it
|
|
10
|
+
* keeps the prompt-cache prefix intact while remaining authoritative for the model.
|
|
11
|
+
* See output-style.ts for the rules and appendOutputStyle for composition.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { appendOutputStyle } from "./output-style.ts";
|
|
16
|
+
|
|
17
|
+
export { OUTPUT_STYLE, OUTPUT_STYLE_MARKER, appendOutputStyle } from "./output-style.ts";
|
|
18
|
+
|
|
19
|
+
export default function outputStyleExtension(pi: ExtensionAPI): void {
|
|
20
|
+
pi.on("before_agent_start", (event) => {
|
|
21
|
+
return { systemPrompt: appendOutputStyle(event.systemPrompt) };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
## Output style (agent-cortex)
|
|
2
|
+
|
|
3
|
+
How every reply is written. These rules shape the reply, never the work: do all
|
|
4
|
+
the analysis and tool calls the task needs, then report the result this way. If a
|
|
5
|
+
rule would delete the answer itself, the task wins; the shape stays.
|
|
6
|
+
|
|
7
|
+
### Lead with the action
|
|
8
|
+
|
|
9
|
+
- The first line is the answer, the command, the path, or the next action. Not
|
|
10
|
+
context, not a plan, not "Let me…".
|
|
11
|
+
- Number multi-step work. One bounded action per step.
|
|
12
|
+
- End with one concrete next step. Omit it when the work is finished.
|
|
13
|
+
|
|
14
|
+
### Keep the prose short
|
|
15
|
+
|
|
16
|
+
- No preamble ("Great question", "I'll now…"), no recap of work just completed,
|
|
17
|
+
no closing offer ("Let me know if…", "Hope this helps").
|
|
18
|
+
- Five items maximum per list. Rank them; group related items.
|
|
19
|
+
- One topic per reply. Name a tangent in one line at the end, then stop.
|
|
20
|
+
|
|
21
|
+
### Write Simplified Technical English (ASD-STE100)
|
|
22
|
+
|
|
23
|
+
- Active voice. Imperative for instructions.
|
|
24
|
+
- Sentence limit: 20 words for instructions, 25 for descriptive text. One
|
|
25
|
+
instruction per sentence.
|
|
26
|
+
- Use the verb, not the noun: "check the file", not "perform a check of the file".
|
|
27
|
+
- One word, one meaning: pick the term for a thing, then reuse it.
|
|
28
|
+
- Literal words only. No idioms, metaphors, or figures of speech ("circle back",
|
|
29
|
+
"under the hood", "ballpark").
|
|
30
|
+
- No marketing adjectives or filler ("seamless", "robust", "comprehensive").
|
|
31
|
+
- Keep articles and subjects. Do not compress into fragments that lose meaning.
|
|
32
|
+
- Give times in concrete units ("about 10 minutes"), never "a bit of work".
|
|
33
|
+
|
|
34
|
+
### Restate the context, never assume it
|
|
35
|
+
|
|
36
|
+
The reader cannot hold earlier turns in working memory.
|
|
37
|
+
|
|
38
|
+
- Never name a bead, ticket, phase, or pipeline stage by ID or number alone. Say
|
|
39
|
+
what it is in plain words first — "the login fix, verify step (bd-142)" — and
|
|
40
|
+
give the ID at most once.
|
|
41
|
+
- After a gap, a topic switch, or a long tool run, restate in one line: what is
|
|
42
|
+
done, what is next.
|
|
43
|
+
- Mark progress concretely: "Step 2 of 4 done: schema updated."
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { OUTPUT_STYLE, OUTPUT_STYLE_MARKER, appendOutputStyle } from "./output-style.ts";
|
|
5
|
+
|
|
6
|
+
// ─── appendOutputStyle ────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
describe("appendOutputStyle", () => {
|
|
9
|
+
it("appends the output-style block after the base prompt", () => {
|
|
10
|
+
const result = appendOutputStyle("You are a helpful agent.");
|
|
11
|
+
|
|
12
|
+
assert.ok(result.startsWith("You are a helpful agent."));
|
|
13
|
+
assert.ok(result.includes(OUTPUT_STYLE));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("preserves a chained base prompt from earlier handlers", () => {
|
|
17
|
+
const base = "Base prompt.\n\n---\nAgent mode instructions.";
|
|
18
|
+
const result = appendOutputStyle(base);
|
|
19
|
+
|
|
20
|
+
assert.ok(result.startsWith(base));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("trims trailing whitespace before appending", () => {
|
|
24
|
+
const result = appendOutputStyle("Base prompt.\n\n");
|
|
25
|
+
|
|
26
|
+
assert.equal(result, `Base prompt.\n\n${OUTPUT_STYLE}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("keeps a block appended by another extension", () => {
|
|
30
|
+
const withGuardrails = "Base prompt.\n\n## Operating discipline (agent-cortex)\n\nRules.\n";
|
|
31
|
+
const result = appendOutputStyle(withGuardrails);
|
|
32
|
+
|
|
33
|
+
assert.ok(result.includes("## Operating discipline (agent-cortex)"));
|
|
34
|
+
assert.ok(result.endsWith(OUTPUT_STYLE));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("is idempotent when applied twice", () => {
|
|
38
|
+
const once = appendOutputStyle("Base prompt.");
|
|
39
|
+
const twice = appendOutputStyle(once);
|
|
40
|
+
|
|
41
|
+
assert.equal(twice, once);
|
|
42
|
+
assert.equal(twice.split(OUTPUT_STYLE_MARKER).length - 1, 1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns the prompt unchanged when the output style is already present", () => {
|
|
46
|
+
const existing = `Base prompt.\n\n${OUTPUT_STYLE}`;
|
|
47
|
+
|
|
48
|
+
assert.equal(appendOutputStyle(existing), existing);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output style for the PI system prompt.
|
|
3
|
+
*
|
|
4
|
+
* The second slice of docs/ideas/improve-pi-system-prompt.md: the reply shape, not
|
|
5
|
+
* the task discipline. Three concerns, in order — lead with the action, write in
|
|
6
|
+
* Simplified Technical English, restate the context the reader cannot remember.
|
|
7
|
+
*
|
|
8
|
+
* The prose lives in output-style.md so one file feeds both harnesses: this
|
|
9
|
+
* extension appends it to the PI system prompt, and the Claude installer wraps it
|
|
10
|
+
* in a managed CLAUDE.md section. It is read once at load, so the appended text is
|
|
11
|
+
* stable across turns and keeps the prompt-cache prefix intact. The marker doubles
|
|
12
|
+
* as an idempotency guard: a chained prompt that already carries the block is
|
|
13
|
+
* returned unchanged.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
export const OUTPUT_STYLE_MARKER = "## Output style (agent-cortex)";
|
|
19
|
+
|
|
20
|
+
export const OUTPUT_STYLE = readFileSync(new URL("./output-style.md", import.meta.url), "utf-8");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Append the output-style block to a system prompt.
|
|
24
|
+
*
|
|
25
|
+
* The base prompt is preserved verbatim (minus trailing whitespace) so callers can
|
|
26
|
+
* chain this after other `before_agent_start` handlers. Idempotent: a prompt that
|
|
27
|
+
* already contains the marker is returned unchanged.
|
|
28
|
+
*/
|
|
29
|
+
export function appendOutputStyle(systemPrompt: string): string {
|
|
30
|
+
if (systemPrompt.includes(OUTPUT_STYLE_MARKER)) return systemPrompt;
|
|
31
|
+
return `${systemPrompt.trimEnd()}\n\n${OUTPUT_STYLE}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-cortex/output-style",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node --test --import tsx/esm output-style.test.ts",
|
|
8
|
+
"typecheck": "tsc --noEmit"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@earendil-works/pi-coding-agent": "^0.78.1",
|
|
12
|
+
"@types/node": "^22.0.0",
|
|
13
|
+
"tsx": "^4.0.0",
|
|
14
|
+
"typescript": "^5.0.0"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@earendil-works/pi-coding-agent": "^0.78.1"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -20,6 +20,7 @@ for await (const chunk of process.stdin) {
|
|
|
20
20
|
raw += chunk;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** @type {{ message?: unknown, title?: unknown, notification_type?: unknown }} */
|
|
23
24
|
let payload = {};
|
|
24
25
|
try {
|
|
25
26
|
payload = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -36,7 +37,13 @@ const type = typeof payload.notification_type === "string" ? payload.notificatio
|
|
|
36
37
|
|
|
37
38
|
process.stdout.write(JSON.stringify({ terminalSequence: pickSequence(title, message, type) }) + "\n");
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Pick an OSC notification escape sequence for the detected terminal.
|
|
42
|
+
* @param {string} title
|
|
43
|
+
* @param {string} message
|
|
44
|
+
* @param {string} type
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
40
47
|
function pickSequence(title, message, type) {
|
|
41
48
|
const body = message.replace(/\u001b/g, "").replace(/\n/g, " ").trim();
|
|
42
49
|
const label = type ? `${type}: ${title}` : title;
|
package/lib/cli.mjs
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of parsing the CLI argv. Every branch returns `command`; the rest are
|
|
3
|
+
* populated only for the commands that accept them.
|
|
4
|
+
* @typedef {object} ParsedArgs
|
|
5
|
+
* @property {"help" | "version" | "install" | "migrate" | "unknown"} command
|
|
6
|
+
* @property {string | null} [harness]
|
|
7
|
+
* @property {string} [name]
|
|
8
|
+
* @property {string} [optionError]
|
|
9
|
+
* @property {boolean} [dryRun]
|
|
10
|
+
* @property {string} [output]
|
|
11
|
+
* @property {string} [pluginRoot]
|
|
12
|
+
* @property {boolean} [requireRegister]
|
|
13
|
+
* @property {boolean} [apply]
|
|
14
|
+
* @property {string} [dir]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** @typedef {{ key: string, boolean?: boolean }} OptionSpec */
|
|
18
|
+
|
|
1
19
|
const SUPPORTED_HARNESSES = ["claude", "pi"];
|
|
2
20
|
|
|
3
21
|
// Options accepted by `install <harness>`. `--output`/`--dry-run` are shared by
|
|
4
22
|
// the claude and pi installers; `--plugin-root` is pi-specific. Unknown or
|
|
5
23
|
// malformed options surface as `optionError` on the parsed result; the CLI
|
|
6
24
|
// prints them and exits 1.
|
|
25
|
+
/** @type {Record<string, OptionSpec>} */
|
|
7
26
|
const INSTALL_OPTIONS = {
|
|
8
27
|
"--dry-run": { key: "dryRun", boolean: true },
|
|
9
28
|
"--output": { key: "output" },
|
|
@@ -11,11 +30,22 @@ const INSTALL_OPTIONS = {
|
|
|
11
30
|
"--require-register": { key: "requireRegister", boolean: true },
|
|
12
31
|
};
|
|
13
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Parse the CLI argv.
|
|
35
|
+
* @param {string[]} argv
|
|
36
|
+
* @returns {ParsedArgs}
|
|
37
|
+
*/
|
|
14
38
|
export function parseArgs(argv) {
|
|
15
39
|
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
16
40
|
return { command: "help" };
|
|
17
41
|
}
|
|
18
42
|
|
|
43
|
+
// Like `--help`, the version flags are only recognised as the first argument,
|
|
44
|
+
// so they never shadow a command's own options.
|
|
45
|
+
if (argv[0] === "--version" || argv[0] === "-V") {
|
|
46
|
+
return { command: "version" };
|
|
47
|
+
}
|
|
48
|
+
|
|
19
49
|
const [command, ...rest] = argv;
|
|
20
50
|
|
|
21
51
|
if (command === "install") {
|
|
@@ -26,6 +56,7 @@ export function parseArgs(argv) {
|
|
|
26
56
|
i = 1;
|
|
27
57
|
}
|
|
28
58
|
|
|
59
|
+
/** @type {Record<string, string | true>} */
|
|
29
60
|
const options = {};
|
|
30
61
|
for (; i < rest.length; i++) {
|
|
31
62
|
const arg = rest[i];
|
|
@@ -57,9 +88,54 @@ export function parseArgs(argv) {
|
|
|
57
88
|
return { command: "install", harness, ...options };
|
|
58
89
|
}
|
|
59
90
|
|
|
91
|
+
if (command === "migrate") {
|
|
92
|
+
// Dry run is the default, so `--apply` is the only state-changing flag;
|
|
93
|
+
// `--dry-run` stays accepted for scripts that pass it explicitly. `--dir`
|
|
94
|
+
// points the ideas step at a workspace that keeps its idea files elsewhere.
|
|
95
|
+
let sawApply = false;
|
|
96
|
+
let sawDryRun = false;
|
|
97
|
+
/** @type {string | undefined} */
|
|
98
|
+
let dir;
|
|
99
|
+
for (let i = 0; i < rest.length; i++) {
|
|
100
|
+
const arg = rest[i];
|
|
101
|
+
if (arg === "--help" || arg === "-h") return { command: "help" };
|
|
102
|
+
if (arg === "--apply") {
|
|
103
|
+
sawApply = true;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (arg === "--dry-run") {
|
|
107
|
+
sawDryRun = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Support both `--dir=<path>` and `--dir <path>`, like install's `--output`.
|
|
111
|
+
const inlineDir = arg.startsWith("--dir=") ? arg.slice("--dir=".length) : null;
|
|
112
|
+
if (inlineDir !== null && inlineDir !== "") {
|
|
113
|
+
dir = inlineDir;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (arg === "--dir" || inlineDir !== null) {
|
|
117
|
+
const value = rest[i + 1];
|
|
118
|
+
if (inlineDir !== null || value == null || value.startsWith("--")) {
|
|
119
|
+
return { command: "migrate", optionError: `Missing value for "--dir". Run "agent-cortex --help" for usage.` };
|
|
120
|
+
}
|
|
121
|
+
dir = value;
|
|
122
|
+
i += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
return { command: "migrate", optionError: `Unknown option "${arg}". Run "agent-cortex --help" for usage.` };
|
|
126
|
+
}
|
|
127
|
+
if (sawApply && sawDryRun) {
|
|
128
|
+
return { command: "migrate", optionError: '"--apply" and "--dry-run" are mutually exclusive.' };
|
|
129
|
+
}
|
|
130
|
+
return dir === undefined ? { command: "migrate", apply: sawApply } : { command: "migrate", apply: sawApply, dir };
|
|
131
|
+
}
|
|
132
|
+
|
|
60
133
|
return { command: "unknown", name: command };
|
|
61
134
|
}
|
|
62
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @returns {string}
|
|
138
|
+
*/
|
|
63
139
|
export function buildHelpText() {
|
|
64
140
|
return `Usage: agent-cortex <command> [options]
|
|
65
141
|
|
|
@@ -125,11 +201,24 @@ Commands:
|
|
|
125
201
|
cannot register the staged package
|
|
126
202
|
(missing from PATH); default warns and
|
|
127
203
|
prints the manual commands
|
|
128
|
-
|
|
204
|
+
migrate Migrate the current beads workspace to the latest
|
|
205
|
+
agent-cortex migration version, applying versioned steps
|
|
206
|
+
in order (repo-agnostic — works in any beads workspace)
|
|
207
|
+
Options:
|
|
208
|
+
(no option) Dry run — print the pending plan, write nothing
|
|
209
|
+
--apply Apply every pending step, verifying each
|
|
210
|
+
--dir <path> Read idea files from <path> instead of
|
|
211
|
+
docs/ideas (step v2's source)
|
|
212
|
+
--help, -h Show this help text
|
|
213
|
+
--version, -V Print the package version`;
|
|
129
214
|
}
|
|
130
215
|
|
|
216
|
+
/**
|
|
217
|
+
* @param {string | null | undefined} harness
|
|
218
|
+
* @returns {{ ok: true } | { ok: false, error: string }}
|
|
219
|
+
*/
|
|
131
220
|
export function validateHarness(harness) {
|
|
132
|
-
if (harness === null) {
|
|
221
|
+
if (harness === null || harness === undefined) {
|
|
133
222
|
return {
|
|
134
223
|
ok: false,
|
|
135
224
|
error: `Missing harness argument. Supported harnesses: ${SUPPORTED_HARNESSES.join(", ")}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaybeeuu/agent-cortex",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Personal PI package with custom agents, skills, and extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -13,12 +13,35 @@
|
|
|
13
13
|
"bin": {
|
|
14
14
|
"agent-cortex": "./bin/agent-cortex.mjs"
|
|
15
15
|
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin/",
|
|
18
|
+
"lib/",
|
|
19
|
+
"scripts/",
|
|
20
|
+
"skills/",
|
|
21
|
+
"agents/",
|
|
22
|
+
"extensions/*.ts",
|
|
23
|
+
"extensions/lib/",
|
|
24
|
+
"extensions/*/*.ts",
|
|
25
|
+
"extensions/*/*.md",
|
|
26
|
+
"extensions/*/package.json",
|
|
27
|
+
"extensions/*/agents/",
|
|
28
|
+
"extensions/*/prompts/",
|
|
29
|
+
"hooks/",
|
|
30
|
+
"claude/",
|
|
31
|
+
"claude-extras/",
|
|
32
|
+
"token-map.json",
|
|
33
|
+
"token-map.README.md",
|
|
34
|
+
"README.md",
|
|
35
|
+
"CHANGELOG.md"
|
|
36
|
+
],
|
|
16
37
|
"publishConfig": {
|
|
17
38
|
"access": "public"
|
|
18
39
|
},
|
|
19
40
|
"devDependencies": {
|
|
20
41
|
"@changesets/cli": "^2.31.1",
|
|
42
|
+
"@types/node": "^22.20.1",
|
|
21
43
|
"tsx": "^4.0.0",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
22
45
|
"yaml": "^2.0.0"
|
|
23
46
|
},
|
|
24
47
|
"pi": {
|
|
@@ -35,13 +58,15 @@
|
|
|
35
58
|
},
|
|
36
59
|
"scripts": {
|
|
37
60
|
"build": "echo 'Build complete — no compilation needed for PI package'",
|
|
38
|
-
"lint": "pnpm typecheck && pnpm check:sync",
|
|
61
|
+
"lint": "pnpm typecheck && pnpm check:sync && pnpm check:skills",
|
|
39
62
|
"check:sync": "node scripts/check-no-sync.mjs",
|
|
63
|
+
"check:skills": "node scripts/check-skill-frontmatter.mjs",
|
|
40
64
|
"test": "node --test test/*.test.mjs && pnpm -r test",
|
|
41
|
-
"typecheck": "pnpm -r typecheck",
|
|
65
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && pnpm -r typecheck",
|
|
66
|
+
"verify": "pnpm typecheck && pnpm test",
|
|
42
67
|
"changeset": "changeset",
|
|
43
68
|
"version-packages": "changeset version",
|
|
44
|
-
"publish-package": "TARBALL=$(pnpm pack | tail -n 1) && npm publish \"$TARBALL\" --provenance --access public",
|
|
69
|
+
"publish-package": "pnpm verify && TARBALL=$(pnpm pack | tail -n 1) && npm publish \"$TARBALL\" --provenance --access public",
|
|
45
70
|
"publish-noop": "echo 'No publish: the merged PR carries the no-publish label.'",
|
|
46
71
|
"release": "changeset publish"
|
|
47
72
|
}
|
|
@@ -14,7 +14,11 @@ const SELF = "scripts/check-no-sync.mjs";
|
|
|
14
14
|
|
|
15
15
|
const SYNC_CALL = /\b\w*Sync\s*\(/;
|
|
16
16
|
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Recursively collect files under dir, skipping this script and node_modules.
|
|
19
|
+
* @param {string} dir
|
|
20
|
+
* @returns {Promise<string[]>}
|
|
21
|
+
*/
|
|
18
22
|
async function collect(dir) {
|
|
19
23
|
const out = [];
|
|
20
24
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Repo convention: every SKILL.md frontmatter must parse and declare a name and
|
|
2
|
+
// description, because PI silently drops a skill whose frontmatter throws. Runs
|
|
3
|
+
// via `pnpm lint` (CI) and locally. Scans the repository root, so both skills/
|
|
4
|
+
// and repo-local .agents/skills/ are covered.
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { checkSkills } from "./lib/skill-frontmatter.mjs";
|
|
8
|
+
|
|
9
|
+
const ROOT = join(fileURLToPath(new URL(".", import.meta.url)), "..");
|
|
10
|
+
|
|
11
|
+
const { checked, failures } = await checkSkills({ root: ROOT });
|
|
12
|
+
|
|
13
|
+
if (failures.length > 0) {
|
|
14
|
+
console.error("✗ Invalid SKILL.md frontmatter — PI would silently drop these skills:");
|
|
15
|
+
for (const { file, errors } of failures) {
|
|
16
|
+
for (const error of errors) console.error(` ${file}: ${error}`);
|
|
17
|
+
}
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`✓ SKILL.md frontmatter valid (${checked} files)`);
|
|
@@ -32,7 +32,49 @@
|
|
|
32
32
|
import { readFile, stat } from "node:fs/promises";
|
|
33
33
|
import { join } from "node:path";
|
|
34
34
|
|
|
35
|
-
/**
|
|
35
|
+
/** @typedef {{ base: string, relative: string }} PathSpec */
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parsed token-map.json. `tools` maps a canonical tool key to its per-harness
|
|
39
|
+
* name (null = the harness has no equivalent); `paths` maps a path key to its
|
|
40
|
+
* per-harness spec (a literal string, or a {base, relative} pair resolved
|
|
41
|
+
* recursively through the same table).
|
|
42
|
+
* @typedef {object} TokenMap
|
|
43
|
+
* @property {number} [version]
|
|
44
|
+
* @property {Record<string, Record<string, string | null>>} tools
|
|
45
|
+
* @property {Record<string, Record<string, string | PathSpec>>} paths
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Options shared by the composer entry points.
|
|
50
|
+
* @typedef {object} ComposeOptions
|
|
51
|
+
* @property {boolean} [dropNullTools]
|
|
52
|
+
* @property {string} [pluginRoot]
|
|
53
|
+
* @property {boolean} [resolveRelativePaths]
|
|
54
|
+
* @property {(message: string) => void} [warn]
|
|
55
|
+
* @property {string} [context]
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parsed and schema-checked harness frontmatter.json.
|
|
60
|
+
* @typedef {object} AgentFrontmatter
|
|
61
|
+
* @property {string} name
|
|
62
|
+
* @property {string} description
|
|
63
|
+
* @property {string[]} tools
|
|
64
|
+
* @property {string} [model]
|
|
65
|
+
* @property {string} [argumentHint]
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A composed agent: its frontmatter plus the substituted body.
|
|
70
|
+
* @typedef {AgentFrontmatter & { body: string }} ComposedAgent
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* True when a path exists (stat succeeds).
|
|
75
|
+
* @param {string} p
|
|
76
|
+
* @returns {Promise<boolean>}
|
|
77
|
+
*/
|
|
36
78
|
async function pathExists(p) {
|
|
37
79
|
try {
|
|
38
80
|
await stat(p);
|
|
@@ -47,6 +89,7 @@ async function pathExists(p) {
|
|
|
47
89
|
const TOOL_RE = /\{\{TOOL:([^}]+)\}\}/g;
|
|
48
90
|
const PATH_RE = /\{\{PATH:([^}]+)\}\}/g;
|
|
49
91
|
|
|
92
|
+
/** @returns {void} */
|
|
50
93
|
function noop() {}
|
|
51
94
|
|
|
52
95
|
/**
|
|
@@ -62,8 +105,8 @@ function noop() {}
|
|
|
62
105
|
*
|
|
63
106
|
* @param {string} content Source text containing {{TOOL:...}}/{{PATH:...}} tokens
|
|
64
107
|
* @param {string} harness Harness id: "claude" | "pi"
|
|
65
|
-
* @param {
|
|
66
|
-
* @param {
|
|
108
|
+
* @param {TokenMap} tokenMap Parsed token-map.json (from loadTokenMap)
|
|
109
|
+
* @param {ComposeOptions} [options] { dropNullTools, pluginRoot, resolveRelativePaths, warn, context }
|
|
67
110
|
* @returns {string}
|
|
68
111
|
*/
|
|
69
112
|
export function substituteTokens(content, harness, tokenMap, options = {}) {
|
|
@@ -100,7 +143,8 @@ export function substituteTokens(content, harness, tokenMap, options = {}) {
|
|
|
100
143
|
* @param {string} root Package root (contains agents/ and token-map.json)
|
|
101
144
|
* @param {string} name Agent directory name (e.g. "ralph", "plan")
|
|
102
145
|
* @param {string} harness Harness id: "claude" | "pi"
|
|
103
|
-
* @
|
|
146
|
+
* @param {ComposeOptions} [options]
|
|
147
|
+
* @returns {Promise<ComposedAgent>}
|
|
104
148
|
*/
|
|
105
149
|
export async function composeAgent(root, name, harness, options = {}) {
|
|
106
150
|
const { dropNullTools = false, pluginRoot, resolveRelativePaths = false, warn = noop } = options;
|
|
@@ -144,14 +188,21 @@ export async function composeAgent(root, name, harness, options = {}) {
|
|
|
144
188
|
const REQUIRED_FIELDS = ["name", "description", "tools"];
|
|
145
189
|
const KNOWN_OPTIONAL_FIELDS = ["model", "argumentHint"];
|
|
146
190
|
|
|
147
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* Parse and schema-check a harness frontmatter.json (strict, per agents/README.md).
|
|
193
|
+
* @param {string} path
|
|
194
|
+
* @param {string} name
|
|
195
|
+
* @param {string} harness
|
|
196
|
+
* @returns {Promise<AgentFrontmatter>}
|
|
197
|
+
*/
|
|
148
198
|
async function parseFrontmatter(path, name, harness) {
|
|
149
199
|
const raw = await readFile(path, "utf-8");
|
|
200
|
+
/** @type {Record<string, unknown>} */
|
|
150
201
|
let fm;
|
|
151
202
|
try {
|
|
152
203
|
fm = JSON.parse(raw);
|
|
153
204
|
} catch (err) {
|
|
154
|
-
throw new Error(`agent "${name}": invalid JSON in ${path}: ${err.message}`);
|
|
205
|
+
throw new Error(`agent "${name}": invalid JSON in ${path}: ${/** @type {Error} */ (err).message}`);
|
|
155
206
|
}
|
|
156
207
|
if (typeof fm !== "object" || fm === null) {
|
|
157
208
|
throw new Error(`agent "${name}": ${path} must be a JSON object`);
|
|
@@ -188,6 +239,11 @@ async function parseFrontmatter(path, name, harness) {
|
|
|
188
239
|
* With the installer options (`pluginRoot`, `resolveRelativePaths`) the
|
|
189
240
|
* plugin_root base and bare relative paths resolve against the actual install
|
|
190
241
|
* layout instead.
|
|
242
|
+
* @param {string} arg
|
|
243
|
+
* @param {string} harness
|
|
244
|
+
* @param {TokenMap} tokenMap
|
|
245
|
+
* @param {ComposeOptions} [opts]
|
|
246
|
+
* @returns {string}
|
|
191
247
|
*/
|
|
192
248
|
function resolvePath(arg, harness, tokenMap, opts = {}) {
|
|
193
249
|
const entry = tokenMap.paths?.[arg];
|
|
@@ -217,9 +273,9 @@ function resolvePath(arg, harness, tokenMap, opts = {}) {
|
|
|
217
273
|
* translation in extensions/agent-modes/discover.ts.
|
|
218
274
|
*
|
|
219
275
|
* @param {string[]} tools Canonical tool names from a harness frontmatter.json
|
|
220
|
-
* @param {
|
|
276
|
+
* @param {TokenMap} tokenMap Parsed token-map.json (from loadTokenMap)
|
|
221
277
|
* @param {string} harness Harness id: "claude" | "pi"
|
|
222
|
-
* @param {
|
|
278
|
+
* @param {ComposeOptions} [options] { warn }
|
|
223
279
|
* @returns {string[]}
|
|
224
280
|
*/
|
|
225
281
|
export function translateToolList(tools, tokenMap, harness, { warn = noop } = {}) {
|
|
@@ -242,7 +298,11 @@ export function translateToolList(tools, tokenMap, harness, { warn = noop } = {}
|
|
|
242
298
|
|
|
243
299
|
// ─── Token map ───────────────────────────────────────────────────────────────
|
|
244
300
|
|
|
245
|
-
/**
|
|
301
|
+
/**
|
|
302
|
+
* Load token-map.json from the package root; missing/incomplete maps hard-fail.
|
|
303
|
+
* @param {string} root
|
|
304
|
+
* @returns {Promise<TokenMap>}
|
|
305
|
+
*/
|
|
246
306
|
export async function loadTokenMap(root) {
|
|
247
307
|
const path = join(root, "token-map.json");
|
|
248
308
|
let raw;
|
package/scripts/lib/fs.mjs
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
import { stat } from "node:fs/promises";
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* True when `p` exists and is a regular file; false on any error.
|
|
17
|
+
* @param {string} p
|
|
18
|
+
* @returns {Promise<boolean>}
|
|
19
|
+
*/
|
|
16
20
|
export async function isFile(p) {
|
|
17
21
|
try {
|
|
18
22
|
return (await stat(p)).isFile();
|
|
@@ -21,7 +25,11 @@ export async function isFile(p) {
|
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* True when `p` exists and is a directory; false on any error.
|
|
30
|
+
* @param {string} p
|
|
31
|
+
* @returns {Promise<boolean>}
|
|
32
|
+
*/
|
|
25
33
|
export async function isDirectory(p) {
|
|
26
34
|
try {
|
|
27
35
|
return (await stat(p)).isDirectory();
|