Merge pull request 'feat: add developer skill — coding conventions + architecture guide' (#548) from feat/541-skill-developer into main

This commit is contained in:
2026-05-26 17:19:16 +00:00
5 changed files with 163 additions and 0 deletions
@@ -10,6 +10,7 @@ import {
cmdSkillArchitecture, cmdSkillArchitecture,
cmdSkillAuthor, cmdSkillAuthor,
cmdSkillCli, cmdSkillCli,
cmdSkillDeveloper,
cmdSkillList, cmdSkillList,
cmdSkillModerator, cmdSkillModerator,
cmdSkillUser, cmdSkillUser,
@@ -27,6 +28,7 @@ describe("skill commands", () => {
expect(result).toContain("actor"); expect(result).toContain("actor");
expect(result).toContain("user"); expect(result).toContain("user");
expect(result).toContain("author"); expect(result).toContain("author");
expect(result).toContain("developer");
for (const name of result) { for (const name of result) {
expect(name).toMatch(/^\S+$/); expect(name).toMatch(/^\S+$/);
} }
@@ -97,6 +99,15 @@ describe("skill commands", () => {
expect(result.length).toBeGreaterThan(500); expect(result.length).toBeGreaterThan(500);
}); });
test("skill developer returns non-empty markdown string", () => {
const result = cmdSkillDeveloper();
expect(typeof result).toBe("string");
expect(result).toContain("Monorepo");
expect(result).toContain("CAS");
expect(result).toContain("Biome");
expect(result.length).toBeGreaterThan(500);
});
test("skill help subcommand is suppressed", () => { test("skill help subcommand is suppressed", () => {
const output = execFileSync("bun", ["src/cli.ts", "skill", "--help"], { const output = execFileSync("bun", ["src/cli.ts", "skill", "--help"], {
cwd: join(__dirname, "..", ".."), cwd: join(__dirname, "..", ".."),
@@ -111,6 +122,7 @@ describe("skill commands", () => {
expect(output).toContain("actor"); expect(output).toContain("actor");
expect(output).toContain("user"); expect(output).toContain("user");
expect(output).toContain("author"); expect(output).toContain("author");
expect(output).toContain("developer");
expect(output).toContain("list"); expect(output).toContain("list");
}); });
}); });
+8
View File
@@ -21,6 +21,7 @@ import {
cmdSkillArchitecture, cmdSkillArchitecture,
cmdSkillAuthor, cmdSkillAuthor,
cmdSkillCli, cmdSkillCli,
cmdSkillDeveloper,
cmdSkillList, cmdSkillList,
cmdSkillModerator, cmdSkillModerator,
cmdSkillUser, cmdSkillUser,
@@ -520,6 +521,13 @@ skill
console.log(cmdSkillAuthor()); console.log(cmdSkillAuthor());
}); });
skill
.command("developer")
.description("Print the developer reference (coding conventions + architecture)")
.action(() => {
console.log(cmdSkillDeveloper());
});
skill skill
.command("moderator") .command("moderator")
.description("Print the moderator reference") .description("Print the moderator reference")
@@ -3,6 +3,7 @@ export {
generateArchitectureReference as cmdSkillArchitecture, generateArchitectureReference as cmdSkillArchitecture,
generateAuthorReference as cmdSkillAuthor, generateAuthorReference as cmdSkillAuthor,
generateCliReference as cmdSkillCli, generateCliReference as cmdSkillCli,
generateDeveloperReference as cmdSkillDeveloper,
generateModeratorReference as cmdSkillModerator, generateModeratorReference as cmdSkillModerator,
generateUserReference as cmdSkillUser, generateUserReference as cmdSkillUser,
generateYamlReference as cmdSkillYaml, generateYamlReference as cmdSkillYaml,
@@ -16,6 +17,7 @@ const SKILL_NAMES = [
"actor", "actor",
"user", "user",
"author", "author",
"developer",
] as const; ] as const;
export function cmdSkillList(): ReadonlyArray<string> { export function cmdSkillList(): ReadonlyArray<string> {
@@ -0,0 +1,140 @@
export function generateDeveloperReference(): string {
return `# Developer Reference
Guide for contributing to the workflow engine codebase.
## Monorepo Structure
\`\`\`
packages/
workflow-protocol/ # Shared types (WorkflowPayload, StepNodePayload, etc.)
workflow-util/ # Base32, ULID, logger, frontmatter parsing, skill references
workflow-util-agent/ # createAgent factory, context builder, extract pipeline
workflow-agent-hermes/ # uwf-hermes CLI (spawns Hermes chat sessions)
workflow-agent-builtin/ # uwf-builtin CLI (direct LLM calls via OpenAI API)
cli-workflow/ # uwf CLI (moderator, thread/step/cas/config commands)
\`\`\`
Dependency layers (each only imports from packages above it):
\`\`\`
protocol → util → util-agent → agent-hermes / agent-builtin / cli-workflow
\`\`\`
External CAS: \`@uncaged/json-cas\` (store API, hashing, schema validation) + \`@uncaged/json-cas-fs\` (filesystem backend).
## Coding Conventions
### Functional-first
| Rule | Description |
|------|-------------|
| \`type\` over \`interface\` | All type definitions use \`type\` |
| \`function\` over \`class\` | Pure functions + closures, no class |
| No \`this\` | Functions must not depend on \`this\` context |
| No inheritance | No \`extends\`, \`implements\`, \`abstract\` |
| No optional properties | Use \`T \\| null\` instead of \`?:\` |
| Immutability first | Use \`Readonly<T>\`, \`as const\`, avoid mutation |
Classes allowed only when required by third-party libraries or for Error subclasses.
### Error Handling
- \`Result<T, E>\` type for expected failures (\`ok\`/\`err\` constructors from \`@uncaged/workflow-util\`)
- \`throw\` only for unrecoverable bugs
- No try-catch for flow control
### Async
Always \`async/await\`, never \`.then()\` chains.
### Logging
\`console.*\` is banned (Biome \`noConsole\` rule). Use the structured logger:
\`\`\`typescript
import { createLogger } from "@uncaged/workflow-util";
const log = createLogger();
log("4KNMR2PX", "Loading workflow..."); // 8-char Crockford Base32 tag
\`\`\`
Each call site gets a unique hand-written tag. \`grep "4KNMR2PX"\` in logs → instant code location.
CLI package (\`@uncaged/cli-workflow\`) may use \`console.log\` for user-facing output with a biome-ignore comment.
### No Dynamic Import
No \`await import()\` in production code. Always static top-level \`import\`. Test files are exempt.
### Naming
- Workflow names: verb-first kebab-case (\`solve-issue\`, \`review-code\`)
- IDs: Crockford Base32 — CAS hash (XXH64, 13-char), Thread ID (ULID, 26-char)
## Development Workflow
\`\`\`bash
bun install # install all workspace deps
bun run build # tsc --build (all packages)
bun run check # tsc + biome check + lint-log-tags
bun run format # biome format --write
bun test # run all tests
\`\`\`
Before committing: \`bun run check\` + \`bun test\` must both pass.
### Testing
- \`cli-workflow\`: vitest
- Other packages: \`bun test\`
- Test files live in \`__tests__/\` directories
### Publishing
Fixed-mode versioning — all \`@uncaged/*\` packages share the same version number.
\`\`\`bash
bun changeset # describe the change
bun version # bump versions + changelogs
bun release # build + test + publish to npmjs
\`\`\`
## Key Modules
### Moderator (\`cli-workflow/src/moderator/\`)
Status-based graph evaluator. Reads \`graph[lastRole][output.$status]\` to determine the next role. Zero LLM cost.
### Extract Pipeline (\`workflow-util-agent/src/\`)
1. Agent produces frontmatter markdown
2. \`parseFrontmatterMarkdown()\` extracts YAML frontmatter
3. \`tryFrontmatterFastPath()\` validates against role's output schema
4. If fast path fails, retries up to 2 times via agent continue
5. Validated output stored as CAS node
### createAgent Factory (\`workflow-util-agent/src/run.ts\`)
Shared entry point for all agent CLIs. Handles:
- Argument parsing (\`--thread\`, \`--role\`, \`--prompt\`)
- Context building (thread history, workflow definition)
- Output extraction and CAS persistence
- Frontmatter retry loop
### CAS Integration
All data is CAS-addressed via \`@uncaged/json-cas\`:
- \`store.put(schemaHash, data)\` → content hash
- \`store.get(hash)\` → node
- \`validate(store, node)\` → schema check
- Schemas registered at workflow add time
## Commit Convention
\`\`\`
<type>(<scope>): <description>
type: feat | fix | refactor | docs | chore | test
scope: workflow | cli | moderator | agent-kit | hermes | util | protocol
\`\`\`
`;
}
+1
View File
@@ -3,6 +3,7 @@ export { generateArchitectureReference } from "./architecture-reference.js";
export { generateAuthorReference } from "./author-reference.js"; export { generateAuthorReference } from "./author-reference.js";
export { encodeUint64AsCrockford } from "./base32.js"; export { encodeUint64AsCrockford } from "./base32.js";
export { generateCliReference } from "./cli-reference.js"; export { generateCliReference } from "./cli-reference.js";
export { generateDeveloperReference } from "./developer-reference.js";
export { env } from "./env.js"; export { env } from "./env.js";
export type { export type {
AgentFrontmatter, AgentFrontmatter,