@ludecker/aaac 1.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/README.md +60 -0
- package/package.json +38 -0
- package/src/cli.mjs +138 -0
- package/src/generators/generate-commands.mjs +165 -0
- package/src/generators/generate-graph-commands.mjs +63 -0
- package/src/generators/generate-graph.mjs +242 -0
- package/src/lib/copy.mjs +36 -0
- package/src/lib/install.mjs +76 -0
- package/src/lib/paths.mjs +54 -0
- package/templates/cursor/aaac/capabilities/registry.json +106 -0
- package/templates/cursor/aaac/contract-schema.md +66 -0
- package/templates/cursor/aaac/contracts/commands/create-module.yaml +28 -0
- package/templates/cursor/aaac/contracts/commands/fix-bug.yaml +32 -0
- package/templates/cursor/aaac/contracts/commands/update-module.yaml +28 -0
- package/templates/cursor/aaac/contracts/skills/impact-analysis.yaml +17 -0
- package/templates/cursor/aaac/contracts/skills/investigation-lite.yaml +17 -0
- package/templates/cursor/aaac/contracts/skills/investigation.yaml +17 -0
- package/templates/cursor/aaac/contracts/skills/validation.yaml +14 -0
- package/templates/cursor/aaac/dependencies.yaml +14 -0
- package/templates/cursor/aaac/dispatch.md +135 -0
- package/templates/cursor/aaac/fitness-functions.yaml +34 -0
- package/templates/cursor/aaac/governance/gates.json +39 -0
- package/templates/cursor/aaac/graph.project.yaml +161 -0
- package/templates/cursor/aaac/layers.md +93 -0
- package/templates/cursor/aaac/lifecycle/lifecycle.json +78 -0
- package/templates/cursor/aaac/lifecycle/phases.json +19 -0
- package/templates/cursor/aaac/ontology.json +219 -0
- package/templates/cursor/aaac/ontology.md +90 -0
- package/templates/cursor/aaac/project.config.json +3 -0
- package/templates/cursor/aaac/run/RUN.md +72 -0
- package/templates/cursor/aaac/run/schema.json +83 -0
- package/templates/cursor/aaac/state/checkpoints/README.md +20 -0
- package/templates/cursor/agents/boundary-review.md +11 -0
- package/templates/cursor/agents/check-capability-trace.md +18 -0
- package/templates/cursor/agents/dependency-analysis.md +11 -0
- package/templates/cursor/agents/discovery-boundaries.md +11 -0
- package/templates/cursor/agents/discovery-inventory.md +14 -0
- package/templates/cursor/agents/discovery-ssot.md +11 -0
- package/templates/cursor/agents/fallow-check-changed.md +9 -0
- package/templates/cursor/agents/impact-analysis.md +22 -0
- package/templates/cursor/agents/plan-layer-map.md +11 -0
- package/templates/cursor/agents/plan-state-machines.md +11 -0
- package/templates/cursor/agents/release-git.md +36 -0
- package/templates/cursor/agents/system-decomposition.md +11 -0
- package/templates/cursor/agents/unit-test-run.md +19 -0
- package/templates/cursor/policies/implementation.md +8 -0
- package/templates/cursor/policies/master-rules.md +7 -0
- package/templates/cursor/skills/shared/api/SKILL.md +26 -0
- package/templates/cursor/skills/shared/architecture/SKILL.md +25 -0
- package/templates/cursor/skills/shared/architecture/orchestrator/SKILL.md +21 -0
- package/templates/cursor/skills/shared/architecture/refactor-analysis.md +302 -0
- package/templates/cursor/skills/shared/check/SKILL.md +47 -0
- package/templates/cursor/skills/shared/component/SKILL.md +24 -0
- package/templates/cursor/skills/shared/dependency-graph/SKILL.md +38 -0
- package/templates/cursor/skills/shared/discovery/SKILL.md +29 -0
- package/templates/cursor/skills/shared/documentation/SKILL.md +21 -0
- package/templates/cursor/skills/shared/documentation/orchestrator/SKILL.md +26 -0
- package/templates/cursor/skills/shared/documentation/orchestrator/contract.yaml +20 -0
- package/templates/cursor/skills/shared/documentation/write-arch-doc.md +168 -0
- package/templates/cursor/skills/shared/domain/SKILL.md +24 -0
- package/templates/cursor/skills/shared/execution/SKILL.md +34 -0
- package/templates/cursor/skills/shared/fitness-functions/SKILL.md +42 -0
- package/templates/cursor/skills/shared/governance/implementation/SKILL.md +424 -0
- package/templates/cursor/skills/shared/impact-analysis/SKILL.md +44 -0
- package/templates/cursor/skills/shared/integration/SKILL.md +22 -0
- package/templates/cursor/skills/shared/investigation/SKILL.md +46 -0
- package/templates/cursor/skills/shared/investigation/orchestrator/SKILL.md +22 -0
- package/templates/cursor/skills/shared/investigation-lite/SKILL.md +38 -0
- package/templates/cursor/skills/shared/migration/SKILL.md +22 -0
- package/templates/cursor/skills/shared/model/SKILL.md +22 -0
- package/templates/cursor/skills/shared/module-authoring/SKILL.md +29 -0
- package/templates/cursor/skills/shared/module-authoring/authoring-template.md +9 -0
- package/templates/cursor/skills/shared/planning/SKILL.md +30 -0
- package/templates/cursor/skills/shared/platform-release/SKILL.md +46 -0
- package/templates/cursor/skills/shared/platform-release/orchestrator/SKILL.md +51 -0
- package/templates/cursor/skills/shared/platform-release/orchestrator/contract.yaml +29 -0
- package/templates/cursor/skills/shared/platform-release/ship-procedure.md +31 -0
- package/templates/cursor/skills/shared/remove/SKILL.md +28 -0
- package/templates/cursor/skills/shared/reporting/SKILL.md +43 -0
- package/templates/cursor/skills/shared/rollback/SKILL.md +46 -0
- package/templates/cursor/skills/shared/root-cause/SKILL.md +24 -0
- package/templates/cursor/skills/shared/run/SKILL.md +64 -0
- package/templates/cursor/skills/shared/schema/SKILL.md +24 -0
- package/templates/cursor/skills/shared/testing/SKILL.md +24 -0
- package/templates/cursor/skills/shared/testing/orchestrator/SKILL.md +22 -0
- package/templates/cursor/skills/shared/validation/SKILL.md +56 -0
- package/templates/cursor/skills/shared/verbs/_dispatch-utils.md +90 -0
- package/templates/cursor/skills/shared/verbs/_lifecycle.md +87 -0
- package/templates/cursor/skills/shared/verbs/_object-skills.md +60 -0
- package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +22 -0
- package/templates/cursor/skills/shared/verbs/check/orchestrator/contract.yaml +24 -0
- package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +39 -0
- package/templates/cursor/skills/shared/verbs/create/orchestrator/contract.yaml +34 -0
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +33 -0
- package/templates/cursor/skills/shared/verbs/fix/orchestrator/contract.yaml +38 -0
- package/templates/cursor/skills/shared/verbs/release/orchestrator/SKILL.md +29 -0
- package/templates/cursor/skills/shared/verbs/release/orchestrator/contract.yaml +25 -0
- package/templates/cursor/skills/shared/verbs/remove/orchestrator/SKILL.md +25 -0
- package/templates/cursor/skills/shared/verbs/remove/orchestrator/contract.yaml +32 -0
- package/templates/cursor/skills/shared/verbs/review/orchestrator/SKILL.md +24 -0
- package/templates/cursor/skills/shared/verbs/review/orchestrator/contract.yaml +25 -0
- package/templates/cursor/skills/shared/verbs/test/orchestrator/SKILL.md +24 -0
- package/templates/cursor/skills/shared/verbs/test/orchestrator/contract.yaml +25 -0
- package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +34 -0
- package/templates/cursor/skills/shared/verbs/update/orchestrator/contract.yaml +34 -0
- package/templates/cursor/skills/shared/verification/SKILL.md +28 -0
- package/templates/cursor/skills/shared/workflow/SKILL.md +25 -0
- package/templates/docs/agentic_architecture.md +131 -0
- package/templates/docs/architecture.md +5 -0
- package/templates/docs/master_rules.md +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @ludecker/aaac
|
|
2
|
+
|
|
3
|
+
**Agentic Architecture as Code (AAAC)** — a complete agentic architecture framework for Cursor.
|
|
4
|
+
|
|
5
|
+
> Commands are the public API. Skills, agents, and orchestrators are private implementation.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
No `npm` CLI required. Use `npx` or `pnpm dlx`:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @ludecker/aaac@latest init
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm dlx @ludecker/aaac@latest init
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Non-interactive:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @ludecker/aaac@latest init --yes --dir /path/to/your/repo
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What you get
|
|
26
|
+
|
|
27
|
+
- `.cursor/aaac/` — ontology, graph, lifecycle, run model, generators config
|
|
28
|
+
- `.cursor/skills/shared/` — full pipeline (discovery → execute → verify → report)
|
|
29
|
+
- `.cursor/agents/` — 13 generic subagent specs
|
|
30
|
+
- `.cursor/commands/` — ~130 generated slash commands
|
|
31
|
+
- `docs/agentic_architecture.md` — user + maintainer guide
|
|
32
|
+
|
|
33
|
+
Add project-specific **domains** under `.cursor/domains/<slug>/` (see maintainer appendix).
|
|
34
|
+
|
|
35
|
+
## Regenerate
|
|
36
|
+
|
|
37
|
+
After editing `ontology.json` or `graph.project.yaml`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @ludecker/aaac@latest generate
|
|
41
|
+
pnpm dlx @ludecker/aaac@latest generate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Links
|
|
45
|
+
|
|
46
|
+
- [Install guide](https://ludecker.com/guide/install-aaac)
|
|
47
|
+
- [Package on npm](https://www.npmjs.com/package/@ludecker/aaac)
|
|
48
|
+
- [Lüdecker](https://ludecker.com) — reference implementation
|
|
49
|
+
|
|
50
|
+
## Publish (maintainers)
|
|
51
|
+
|
|
52
|
+
Authenticate with a registry token in `.npmrc` (used by pnpm, not the `npm` CLI):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm --filter @ludecker/aaac publish --access public --no-git-checks
|
|
56
|
+
git tag aaac-v1.0.0
|
|
57
|
+
git push origin aaac-v1.0.0
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
CI publishes on `aaac-v*` tags via `.github/workflows/publish-aaac.yml` (`NPM_TOKEN` secret).
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ludecker/aaac",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Agentic Architecture as Code (AAAC) — installable Cursor agent framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Hans-Erik Lydecker",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/eriklydecker/ludecker.git",
|
|
11
|
+
"directory": "packages/aaac"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://ludecker.com/guide/install-aaac",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/eriklydecker/ludecker/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"aaac",
|
|
19
|
+
"agentic-architecture",
|
|
20
|
+
"cursor",
|
|
21
|
+
"ai-agents",
|
|
22
|
+
"skills",
|
|
23
|
+
"commands"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"aaac": "./src/cli.mjs"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"src/",
|
|
30
|
+
"templates/"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import readline from "readline";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { parseArgs } from "./lib/paths.mjs";
|
|
5
|
+
import { installAaac, runGenerators } from "./lib/install.mjs";
|
|
6
|
+
import { resolveCursorRoot } from "./lib/paths.mjs";
|
|
7
|
+
|
|
8
|
+
function printHelp() {
|
|
9
|
+
console.log(`@ludecker/aaac — Agentic Architecture as Code
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
npx @ludecker/aaac@latest init [options]
|
|
13
|
+
pnpm dlx @ludecker/aaac@latest init [options]
|
|
14
|
+
aaac generate [options]
|
|
15
|
+
|
|
16
|
+
Commands:
|
|
17
|
+
init Copy AAAC kernel into .cursor/ and docs/ (default)
|
|
18
|
+
generate Regenerate graph.yaml and commands from ontology
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--dir <path> Target project directory (default: cwd)
|
|
22
|
+
--yes, -y Non-interactive defaults
|
|
23
|
+
--force Backup existing .cursor/ and replace
|
|
24
|
+
|
|
25
|
+
Install (no npm CLI required):
|
|
26
|
+
npx @ludecker/aaac@latest init
|
|
27
|
+
pnpm dlx @ludecker/aaac@latest init
|
|
28
|
+
|
|
29
|
+
Docs: https://ludecker.com/guide/install-aaac
|
|
30
|
+
Package: https://www.npmjs.com/package/@ludecker/aaac
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ask(question, defaultValue) {
|
|
35
|
+
const rl = readline.createInterface({
|
|
36
|
+
input: process.stdin,
|
|
37
|
+
output: process.stdout,
|
|
38
|
+
});
|
|
39
|
+
const prompt = defaultValue
|
|
40
|
+
? `${question} [${defaultValue}]: `
|
|
41
|
+
: `${question}: `;
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
rl.question(prompt, (answer) => {
|
|
44
|
+
rl.close();
|
|
45
|
+
const trimmed = answer.trim();
|
|
46
|
+
resolve(trimmed || defaultValue || "");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function promptInitOptions(args) {
|
|
52
|
+
if (args.yes) {
|
|
53
|
+
const dir = args.dir ? path.resolve(args.dir) : process.cwd();
|
|
54
|
+
const base = path.basename(dir);
|
|
55
|
+
return {
|
|
56
|
+
targetDir: dir,
|
|
57
|
+
projectName: base || "my-project",
|
|
58
|
+
docsRoot: "docs",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const targetDir = args.dir
|
|
63
|
+
? path.resolve(args.dir)
|
|
64
|
+
: await ask("Project directory", process.cwd());
|
|
65
|
+
const projectName = await ask(
|
|
66
|
+
"Project name",
|
|
67
|
+
path.basename(targetDir) || "my-project",
|
|
68
|
+
);
|
|
69
|
+
const docsRoot = await ask("Docs folder (relative to project)", "docs");
|
|
70
|
+
|
|
71
|
+
return { targetDir, projectName, docsRoot };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function cmdInit(args) {
|
|
75
|
+
const options = await promptInitOptions(args);
|
|
76
|
+
console.log(`\nInstalling AAAC into ${options.targetDir}...\n`);
|
|
77
|
+
|
|
78
|
+
const { cursorDest, docsDest } = installAaac({
|
|
79
|
+
targetDir: options.targetDir,
|
|
80
|
+
projectName: options.projectName,
|
|
81
|
+
docsRoot: options.docsRoot,
|
|
82
|
+
force: args.force,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log(`
|
|
86
|
+
AAAC installed.
|
|
87
|
+
|
|
88
|
+
.cursor/ → ${cursorDest}
|
|
89
|
+
docs/ → ${docsDest}
|
|
90
|
+
|
|
91
|
+
Next steps:
|
|
92
|
+
1. Open the project in Cursor
|
|
93
|
+
2. Create ${options.docsRoot}/master_rules.md and ${options.docsRoot}/architecture.md if missing
|
|
94
|
+
3. Try /review-architecture or /check-architecture
|
|
95
|
+
4. Read ${options.docsRoot}/agentic_architecture.md — Part 2 for adding domains
|
|
96
|
+
|
|
97
|
+
Regenerate after ontology changes:
|
|
98
|
+
pnpm dlx @ludecker/aaac@latest generate
|
|
99
|
+
npx @ludecker/aaac@latest generate
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function cmdGenerate(args) {
|
|
104
|
+
const targetDir = args.dir ? path.resolve(args.dir) : process.cwd();
|
|
105
|
+
const cursorRoot = resolveCursorRoot(path.join(targetDir, ".cursor"));
|
|
106
|
+
console.log(`Regenerating AAAC graph at ${cursorRoot}...`);
|
|
107
|
+
runGenerators(cursorRoot);
|
|
108
|
+
console.log("Done.");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const argv = process.argv.slice(2);
|
|
113
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
114
|
+
printHelp();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const args = parseArgs(process.argv);
|
|
119
|
+
const sub = argv.find((a) => !a.startsWith("-")) ?? "init";
|
|
120
|
+
|
|
121
|
+
if (sub === "init") {
|
|
122
|
+
await cmdInit(args);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (sub === "generate") {
|
|
126
|
+
cmdGenerate(args);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.error(`Unknown command: ${sub}`);
|
|
131
|
+
printHelp();
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
main().catch((err) => {
|
|
136
|
+
console.error(err.message ?? err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Regenerates .cursor/commands/<verb>-<object>.md from ontology.json
|
|
4
|
+
* Prunes stale command files. manual_commands from aaac/project.config.json
|
|
5
|
+
*/
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { aaacDir, parseArgs, resolveCursorRoot } from "../lib/paths.mjs";
|
|
9
|
+
|
|
10
|
+
const args = parseArgs(process.argv);
|
|
11
|
+
const cursorRoot = resolveCursorRoot(args.root);
|
|
12
|
+
const aaac = aaacDir(cursorRoot);
|
|
13
|
+
const commandsDir = path.join(cursorRoot, "commands");
|
|
14
|
+
fs.mkdirSync(commandsDir, { recursive: true });
|
|
15
|
+
|
|
16
|
+
const data = JSON.parse(
|
|
17
|
+
fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const projectConfigPath = path.join(aaac, "project.config.json");
|
|
21
|
+
const projectConfig = fs.existsSync(projectConfigPath)
|
|
22
|
+
? JSON.parse(fs.readFileSync(projectConfigPath, "utf8"))
|
|
23
|
+
: { manual_commands: [] };
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
verbs,
|
|
27
|
+
objects,
|
|
28
|
+
invalid_pairs,
|
|
29
|
+
command_overrides,
|
|
30
|
+
command_aliases,
|
|
31
|
+
} = data;
|
|
32
|
+
|
|
33
|
+
const invalid = new Set(invalid_pairs.map(([v, o]) => `${v}-${o}`));
|
|
34
|
+
|
|
35
|
+
const KEEP_EXTRA = new Set(projectConfig.manual_commands ?? []);
|
|
36
|
+
|
|
37
|
+
const EXCEPTION_CMD = {
|
|
38
|
+
"fix-bug": {
|
|
39
|
+
layer: "product",
|
|
40
|
+
description: "defect repair (resolver; not in 12-object matrix)",
|
|
41
|
+
},
|
|
42
|
+
"review-incident": {
|
|
43
|
+
layer: "system",
|
|
44
|
+
description: "production incident investigation",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function objectMeta(object, cmd) {
|
|
49
|
+
if (EXCEPTION_CMD[cmd]) return EXCEPTION_CMD[cmd];
|
|
50
|
+
const o = objects[object];
|
|
51
|
+
if (!o) return { layer: null, description: object };
|
|
52
|
+
if (typeof o === "string") return { layer: null, description: o };
|
|
53
|
+
return { layer: o.layer ?? null, description: o.description ?? object };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cap(s) {
|
|
57
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function orchestratorHint(cmd, entry) {
|
|
61
|
+
if (entry?.alias) return `alias → \`${entry.alias}\``;
|
|
62
|
+
if (entry?.resolver) {
|
|
63
|
+
const fallback =
|
|
64
|
+
cmd === "fix-bug"
|
|
65
|
+
? " — unknown slug → `verb-fix` + object `feature`"
|
|
66
|
+
: "";
|
|
67
|
+
return `resolver \`${entry.resolver}\`${fallback}`;
|
|
68
|
+
}
|
|
69
|
+
const verb = cmd.split("-")[0];
|
|
70
|
+
const orch = entry?.orchestrator;
|
|
71
|
+
const dedicated = new Set([
|
|
72
|
+
"update-doc",
|
|
73
|
+
"review-module",
|
|
74
|
+
"review-incident",
|
|
75
|
+
"test-function",
|
|
76
|
+
"release-app",
|
|
77
|
+
"write-article",
|
|
78
|
+
]);
|
|
79
|
+
if (cmd === "update-architecture" || (orch && dedicated.has(orch))) {
|
|
80
|
+
const id = cmd === "update-architecture" ? "update-doc" : orch;
|
|
81
|
+
return `orchestrator \`${id}\` (see graph \`orchestrators.${id}.path\`)`;
|
|
82
|
+
}
|
|
83
|
+
const object = cmd.split("-").slice(1).join("-");
|
|
84
|
+
return `[skills/shared/verbs/${verb}/orchestrator/SKILL.md](../skills/shared/verbs/${verb}/orchestrator/SKILL.md) (object: \`${object}\`)`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function domainLine(cmd) {
|
|
88
|
+
if (cmd === "review-incident" || cmd.endsWith("-function")) {
|
|
89
|
+
return "Domain optional.";
|
|
90
|
+
}
|
|
91
|
+
if (cmd.endsWith("-domain") || cmd.endsWith("-module")) {
|
|
92
|
+
return "Domain slug required (bounded context).";
|
|
93
|
+
}
|
|
94
|
+
return "Domain slug recommended.";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function writeCmd(cmd, entry = null) {
|
|
98
|
+
const parts = cmd.split("-");
|
|
99
|
+
const verb = parts[0];
|
|
100
|
+
let object = parts.slice(1).join("-");
|
|
101
|
+
const vDesc = verbs[verb] ?? verb;
|
|
102
|
+
if (entry?.alias) {
|
|
103
|
+
const canonParts = entry.alias.split("-");
|
|
104
|
+
object = canonParts.slice(1).join("-");
|
|
105
|
+
}
|
|
106
|
+
const { layer, description } = objectMeta(object, entry?.alias ?? cmd);
|
|
107
|
+
const aliasNote = entry?.alias
|
|
108
|
+
? ` (alias \`${cmd}\` → \`${entry.alias}\`)`
|
|
109
|
+
: "";
|
|
110
|
+
const domainArg =
|
|
111
|
+
cmd === "review-incident" || cmd.endsWith("-function") ? "" : " <domain>";
|
|
112
|
+
const layerLine = layer ? `**Layer:** ${layer} \n` : "";
|
|
113
|
+
const body = `# ${cmd}
|
|
114
|
+
|
|
115
|
+
AAAC: \`/${cmd}${domainArg} "<intent>"\`
|
|
116
|
+
|
|
117
|
+
${layerLine}**${cap(vDesc)}** a **${object}**${aliasNote} — ${description}.
|
|
118
|
+
|
|
119
|
+
## Dispatch
|
|
120
|
+
|
|
121
|
+
1. [.cursor/aaac/dispatch.md](../aaac/dispatch.md)
|
|
122
|
+
2. [.cursor/aaac/graph.yaml](../aaac/graph.yaml) — **\`${cmd}\`**
|
|
123
|
+
3. ${orchestratorHint(cmd, entry ?? {})}
|
|
124
|
+
|
|
125
|
+
${domainLine(cmd)}
|
|
126
|
+
`;
|
|
127
|
+
fs.writeFileSync(path.join(commandsDir, `${cmd}.md`), body);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const written = new Set();
|
|
131
|
+
|
|
132
|
+
for (const [cmd, entry] of Object.entries(command_overrides)) {
|
|
133
|
+
writeCmd(cmd, entry);
|
|
134
|
+
written.add(cmd);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const [alias, canonical] of Object.entries(command_aliases ?? {})) {
|
|
138
|
+
if (written.has(alias)) continue;
|
|
139
|
+
writeCmd(alias, { alias: canonical });
|
|
140
|
+
written.add(alias);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const verb of Object.keys(verbs)) {
|
|
144
|
+
for (const object of Object.keys(objects)) {
|
|
145
|
+
const cmd = `${verb}-${object}`;
|
|
146
|
+
if (invalid.has(cmd) || written.has(cmd)) continue;
|
|
147
|
+
writeCmd(cmd, { orchestrator: `verb-${verb}`, object });
|
|
148
|
+
written.add(cmd);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let pruned = 0;
|
|
153
|
+
if (fs.existsSync(commandsDir)) {
|
|
154
|
+
for (const file of fs.readdirSync(commandsDir)) {
|
|
155
|
+
if (!file.endsWith(".md") || KEEP_EXTRA.has(file)) continue;
|
|
156
|
+
if (!written.has(file.replace(/\.md$/, ""))) {
|
|
157
|
+
fs.unlinkSync(path.join(commandsDir, file));
|
|
158
|
+
pruned++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
fs.mkdirSync(commandsDir, { recursive: true });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(`Wrote ${written.size} command files, pruned ${pruned} stale.`);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Prints graph.yaml `commands:` block from ontology.json */
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { aaacDir, parseArgs, resolveCursorRoot } from "../lib/paths.mjs";
|
|
6
|
+
|
|
7
|
+
const args = parseArgs(process.argv);
|
|
8
|
+
const cursorRoot = resolveCursorRoot(args.root);
|
|
9
|
+
const aaac = aaacDir(cursorRoot);
|
|
10
|
+
|
|
11
|
+
const data = JSON.parse(
|
|
12
|
+
fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
|
|
13
|
+
);
|
|
14
|
+
const { verbs, objects, invalid_pairs, command_overrides, command_aliases } =
|
|
15
|
+
data;
|
|
16
|
+
const invalid = new Set(invalid_pairs.map(([v, o]) => `${v}-${o}`));
|
|
17
|
+
|
|
18
|
+
const lines = ["commands:"];
|
|
19
|
+
const done = new Set();
|
|
20
|
+
|
|
21
|
+
function emit(cmd, entry) {
|
|
22
|
+
done.add(cmd);
|
|
23
|
+
if (entry.alias) {
|
|
24
|
+
lines.push(` ${cmd}:`);
|
|
25
|
+
lines.push(` alias: ${entry.alias}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (entry.resolver) {
|
|
29
|
+
lines.push(` ${cmd}:`);
|
|
30
|
+
lines.push(` resolver: ${entry.resolver}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (entry.orchestrator) {
|
|
34
|
+
lines.push(` ${cmd}:`);
|
|
35
|
+
lines.push(` orchestrator: ${entry.orchestrator}`);
|
|
36
|
+
if (entry.object) lines.push(` object: ${entry.object}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const [cmd, entry] of Object.entries(command_overrides)) {
|
|
41
|
+
emit(cmd, entry);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const verb of Object.keys(verbs)) {
|
|
45
|
+
for (const object of Object.keys(objects)) {
|
|
46
|
+
const cmd = `${verb}-${object}`;
|
|
47
|
+
if (invalid.has(cmd) || done.has(cmd)) continue;
|
|
48
|
+
done.add(cmd);
|
|
49
|
+
const orch = verb === "check" ? "verb-check" : `verb-${verb}`;
|
|
50
|
+
lines.push(` ${cmd}:`);
|
|
51
|
+
lines.push(` orchestrator: ${orch}`);
|
|
52
|
+
lines.push(` object: ${object}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const [alias, canonical] of Object.entries(command_aliases ?? {})) {
|
|
57
|
+
if (done.has(alias) || alias === canonical) continue;
|
|
58
|
+
done.add(alias);
|
|
59
|
+
lines.push(` ${alias}:`);
|
|
60
|
+
lines.push(` alias: ${canonical}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(lines.join("\n"));
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Writes .cursor/aaac/graph.yaml from ontology + lifecycle + graph.project.yaml */
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import {
|
|
8
|
+
aaacDir,
|
|
9
|
+
parseArgs,
|
|
10
|
+
resolveCursorRoot,
|
|
11
|
+
packageGeneratorsDir,
|
|
12
|
+
} from "../lib/paths.mjs";
|
|
13
|
+
|
|
14
|
+
const INJECT_MARKER = "{{INJECT_OBJECT_BLOCKS}}";
|
|
15
|
+
|
|
16
|
+
const args = parseArgs(process.argv);
|
|
17
|
+
const cursorRoot = resolveCursorRoot(args.root);
|
|
18
|
+
const aaac = aaacDir(cursorRoot);
|
|
19
|
+
const generatorsDir = packageGeneratorsDir();
|
|
20
|
+
|
|
21
|
+
const commandsBlock = execSync(
|
|
22
|
+
`node "${path.join(generatorsDir, "generate-graph-commands.mjs")}" --root "${cursorRoot}"`,
|
|
23
|
+
{ encoding: "utf8" },
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const data = JSON.parse(
|
|
27
|
+
fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const lifecycle = JSON.parse(
|
|
31
|
+
fs.readFileSync(path.join(aaac, "lifecycle/lifecycle.json"), "utf8"),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const gates = JSON.parse(
|
|
35
|
+
fs.readFileSync(path.join(aaac, "governance/gates.json"), "utf8"),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const capabilityRegistry = JSON.parse(
|
|
39
|
+
fs.readFileSync(path.join(aaac, "capabilities/registry.json"), "utf8"),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const projectTail = fs.readFileSync(
|
|
43
|
+
path.join(aaac, "graph.project.yaml"),
|
|
44
|
+
"utf8",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
function providerSkillId(provider) {
|
|
48
|
+
if (typeof provider === "string") return provider;
|
|
49
|
+
if (provider.type === "skill") return provider.id;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveCapabilities(capIds) {
|
|
54
|
+
const skills = new Set();
|
|
55
|
+
for (const id of capIds ?? []) {
|
|
56
|
+
const cap = capabilityRegistry.capabilities[id];
|
|
57
|
+
if (!cap) throw new Error(`Unknown capability in ontology: ${id}`);
|
|
58
|
+
for (const provider of cap.providers) {
|
|
59
|
+
const skillId = providerSkillId(provider);
|
|
60
|
+
if (skillId) skills.add(skillId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return [...skills];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function composeRuntimePhases(verbDef) {
|
|
67
|
+
const work = verbDef.work_phases ?? [];
|
|
68
|
+
if (!verbDef.gate_stack) return work;
|
|
69
|
+
const gatePhases = gates.stacks[verbDef.gate_stack] ?? [];
|
|
70
|
+
const executeIdx = work.indexOf("execute");
|
|
71
|
+
if (executeIdx === -1) {
|
|
72
|
+
const reportIdx = work.indexOf("report");
|
|
73
|
+
if (reportIdx <= 0) {
|
|
74
|
+
return work.length <= 1
|
|
75
|
+
? [...work, ...gatePhases]
|
|
76
|
+
: [...work.slice(0, -1), ...gatePhases, work[work.length - 1]];
|
|
77
|
+
}
|
|
78
|
+
return [...work.slice(0, reportIdx), ...gatePhases, ...work.slice(reportIdx)];
|
|
79
|
+
}
|
|
80
|
+
return [...work.slice(0, executeIdx), ...gatePhases, ...work.slice(executeIdx)];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const resolvedObjectSkills = Object.fromEntries(
|
|
84
|
+
Object.entries(data.object_capabilities ?? {}).map(([object, caps]) => [
|
|
85
|
+
object,
|
|
86
|
+
resolveCapabilities(caps),
|
|
87
|
+
]),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const resolvedObjectSkillVerbs = Object.fromEntries(
|
|
91
|
+
Object.entries(data.object_capability_verbs ?? {}).map(([object, verbs]) => [
|
|
92
|
+
object,
|
|
93
|
+
Object.fromEntries(
|
|
94
|
+
Object.entries(verbs).map(([verb, caps]) => [
|
|
95
|
+
verb,
|
|
96
|
+
resolveCapabilities(caps),
|
|
97
|
+
]),
|
|
98
|
+
),
|
|
99
|
+
]),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const objectCapabilitiesBlock = Object.entries(data.object_capabilities ?? {})
|
|
103
|
+
.map(([object, caps]) =>
|
|
104
|
+
caps.length ? ` ${object}: [${caps.join(", ")}]` : ` ${object}: []`,
|
|
105
|
+
)
|
|
106
|
+
.join("\n");
|
|
107
|
+
|
|
108
|
+
const objectSkillsBlock = Object.entries(resolvedObjectSkills)
|
|
109
|
+
.map(([object, skills]) =>
|
|
110
|
+
skills.length ? ` ${object}: [${skills.join(", ")}]` : ` ${object}: []`,
|
|
111
|
+
)
|
|
112
|
+
.join("\n");
|
|
113
|
+
|
|
114
|
+
const objectSkillVerbsBlock = Object.entries(resolvedObjectSkillVerbs)
|
|
115
|
+
.map(([object, verbs]) => {
|
|
116
|
+
const inner = Object.entries(verbs)
|
|
117
|
+
.map(([verb, skills]) =>
|
|
118
|
+
skills.length
|
|
119
|
+
? ` ${verb}: [${skills.join(", ")}]`
|
|
120
|
+
: ` ${verb}: []`,
|
|
121
|
+
)
|
|
122
|
+
.join("\n");
|
|
123
|
+
return ` ${object}:\n${inner}`;
|
|
124
|
+
})
|
|
125
|
+
.join("\n");
|
|
126
|
+
|
|
127
|
+
const verbWorkPhasesBlock = Object.entries(lifecycle.verbs ?? {})
|
|
128
|
+
.map(
|
|
129
|
+
([verb, def]) => ` ${verb}: [${(def.work_phases ?? []).join(", ")}]`,
|
|
130
|
+
)
|
|
131
|
+
.join("\n");
|
|
132
|
+
|
|
133
|
+
const verbGateStackBlock = Object.entries(lifecycle.verbs ?? {})
|
|
134
|
+
.map(
|
|
135
|
+
([verb, def]) =>
|
|
136
|
+
` ${verb}: ${def.gate_stack ?? "null"}`,
|
|
137
|
+
)
|
|
138
|
+
.join("\n");
|
|
139
|
+
|
|
140
|
+
const verbRuntimeBlock = Object.entries(lifecycle.verbs ?? {})
|
|
141
|
+
.map(([verb, def]) => {
|
|
142
|
+
const phases = Array.isArray(def) ? def : composeRuntimePhases(def);
|
|
143
|
+
return ` ${verb}: [${phases.join(", ")}]`;
|
|
144
|
+
})
|
|
145
|
+
.join("\n");
|
|
146
|
+
|
|
147
|
+
const governanceGateStacksBlock = Object.entries(gates.stacks ?? {})
|
|
148
|
+
.map(([name, phases]) => ` ${name}: [${phases.join(", ")}]`)
|
|
149
|
+
.join("\n");
|
|
150
|
+
|
|
151
|
+
const layerComment = Object.entries(data.layers ?? {})
|
|
152
|
+
.sort((a, b) => a[1].order - b[1].order)
|
|
153
|
+
.map(([name, L]) => `# ${name}: ${L.objects.join(", ")}`)
|
|
154
|
+
.join("\n");
|
|
155
|
+
|
|
156
|
+
const confidenceBlock = Object.entries(data.confidence ?? {})
|
|
157
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
158
|
+
.join("\n");
|
|
159
|
+
|
|
160
|
+
const objectMaturityBlock = Object.entries(data.object_maturity ?? {})
|
|
161
|
+
.map(([object, level]) => ` ${object}: ${level}`)
|
|
162
|
+
.join("\n");
|
|
163
|
+
|
|
164
|
+
const maturityRulesBlock = Object.entries(data.maturity_rules ?? {})
|
|
165
|
+
.map(([level, rule]) => {
|
|
166
|
+
const phases = (rule.requires_phases ?? [])
|
|
167
|
+
.map((p) => ` - ${p}`)
|
|
168
|
+
.join("\n");
|
|
169
|
+
return ` ${level}:\n requires_phases:\n${phases || " []"}\n investigation: ${rule.investigation ?? "lite_on_create_update_deep_on_fix"}`;
|
|
170
|
+
})
|
|
171
|
+
.join("\n");
|
|
172
|
+
|
|
173
|
+
const injectBlock = `object_capabilities:
|
|
174
|
+
${objectCapabilitiesBlock}
|
|
175
|
+
|
|
176
|
+
object_skills:
|
|
177
|
+
${objectSkillsBlock}
|
|
178
|
+
|
|
179
|
+
object_skill_verbs:
|
|
180
|
+
${objectSkillVerbsBlock}
|
|
181
|
+
`;
|
|
182
|
+
|
|
183
|
+
const header = `version: 1
|
|
184
|
+
|
|
185
|
+
# AAAC execution graph — SSOT for command → orchestrator → skills → agents.
|
|
186
|
+
# Regenerate: pnpm aaac:generate (or npx @ludecker/aaac generate)
|
|
187
|
+
# Path convention: agents/, policies/, skills/, domains/ are relative to .cursor/
|
|
188
|
+
# Ontology: .cursor/aaac/ontology.json — layers (code → data → product → system):
|
|
189
|
+
${layerComment}
|
|
190
|
+
|
|
191
|
+
verbs: [${Object.keys(data.verbs).join(", ")}]
|
|
192
|
+
objects: [${Object.keys(data.objects).join(", ")}]
|
|
193
|
+
|
|
194
|
+
invalid_pairs:
|
|
195
|
+
${data.invalid_pairs.map(([v, o]) => ` - [${v}, ${o}]`).join("\n")}
|
|
196
|
+
|
|
197
|
+
verb_work_phases:
|
|
198
|
+
${verbWorkPhasesBlock}
|
|
199
|
+
|
|
200
|
+
verb_gate_stack:
|
|
201
|
+
${verbGateStackBlock}
|
|
202
|
+
|
|
203
|
+
governance_gate_stacks:
|
|
204
|
+
${governanceGateStacksBlock}
|
|
205
|
+
|
|
206
|
+
# Composed runtime: work through plan + gate stack + execute onward (see lifecycle.json runtime_compose)
|
|
207
|
+
verb_runtime:
|
|
208
|
+
${verbRuntimeBlock}
|
|
209
|
+
|
|
210
|
+
confidence:
|
|
211
|
+
${confidenceBlock}
|
|
212
|
+
|
|
213
|
+
object_maturity:
|
|
214
|
+
${objectMaturityBlock}
|
|
215
|
+
|
|
216
|
+
maturity_rules:
|
|
217
|
+
${maturityRulesBlock}
|
|
218
|
+
|
|
219
|
+
# Layer SSOT refs (see aaac/layers.md)
|
|
220
|
+
lifecycle: aaac/lifecycle/lifecycle.json
|
|
221
|
+
lifecycle_phases: aaac/lifecycle/phases.json
|
|
222
|
+
governance_gates: aaac/governance/gates.json
|
|
223
|
+
run: aaac/run/schema.json
|
|
224
|
+
capabilities: aaac/capabilities/registry.json
|
|
225
|
+
dependencies: aaac/dependencies.yaml
|
|
226
|
+
fitness_functions: aaac/fitness-functions.yaml
|
|
227
|
+
state: aaac/state/runs/
|
|
228
|
+
observability: aaac/run/schema.json
|
|
229
|
+
contracts: aaac/contracts/
|
|
230
|
+
|
|
231
|
+
`;
|
|
232
|
+
|
|
233
|
+
let tail = projectTail.trim();
|
|
234
|
+
if (tail.includes(INJECT_MARKER)) {
|
|
235
|
+
tail = tail.replace(INJECT_MARKER, injectBlock.trim());
|
|
236
|
+
} else {
|
|
237
|
+
tail = `${tail}\n\n${injectBlock.trim()}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const out = `${header}${commandsBlock}\n\n${tail}\n`;
|
|
241
|
+
fs.writeFileSync(path.join(aaac, "graph.yaml"), out);
|
|
242
|
+
console.log("Wrote graph.yaml");
|