Skip to content

Commit 8e2cecf

Browse files
authored
feat(project): tree-based scaffolding for agentcore project create (#1809)
* feat(assets): add AssetManager and CDK scaffold templates Introduce the asset embedding + rendering subsystem: AssetManager renders asset trees from either Bun.embeddedFiles (compiled binary) or the source filesystem (dev/node), applying Handlebars templating with HTML escaping disabled and atomic writes. - src/fs/atomicWrite: central temp-file + rename write util - src/assetManager: AssetManager, types, barrel, tests, snapshot baseline - src/assets/cdk: CDK project scaffold templates (payload, not source) - tsconfig/bunfig/oxlint/prettier: exclude src/assets from all tooling - add handlebars dependency * Remove comment Remove comment about directory injection for testability. * fix(assets): strict template rendering and deterministic sort Address review feedback on the AssetManager: - Handlebars strict mode: a template referencing an undefined variable now throws instead of silently rendering an empty string into a generated project file. - drop the speculative `docker` branch from the ignore-template rename; only git/npm ignores ship as assets today. - use code-unit ordering for embedded files so both list paths (embedded and filesystem) sort identically and deterministically. * refactor(assets): rename to kebab-case, doc types, sync cdk pins Address review feedback from @Hweinstock: - rename AssetManager.ts -> manager.ts (only non-React .ts in the repo with an uppercase name; feature dirs use role-named lowercase files like core/project/manager.tsx). - document AssetFile vs EmbeddedFile so the distinction between a resolved tree entry and a raw Bun.embeddedFiles blob is explicit. - sync src/assets/cdk/package.json to the minor-version pins landed in #1777 (aws-cdk-lib ~2.261.0, @aws/agentcore-cdk 0.1.0-alpha.45, etc.). * docs(assets): note cdk README commands are provisional Add a source-only TODO (HTML comment, invisible in the rendered README shipped to generated projects) flagging that the agentcore deploy/status commands may need a project prefix once the project CLI surface is final. * refactor(project): tree-based scaffolding with runtime-blind asset source Replace AssetManager/render with a project tree: a runtime-blind writer (writeTree over dir/file nodes) fed by a single asset-access seam (Source) that reads from disk under Node and from Bun.embeddedFiles in the compiled executable. scripts/build.ts embeds assets by naming so no per-file import attributes are needed. - project create takes a required --project-name and scaffolds into a fresh ./<name>/ directory (writeTree refuses to clobber existing files) - Template carries a spec fragment spread over the fixed base, so a new template (runtimes/memories/harnesses) is a pure-data entry * fix(project): address review feedback on tree-based scaffolding - write agentcore.json inside agentcore/ so ConfigIO project discovery finds it - mirror the CDK schema's reserved-name validation on --project-name - rename the hello world runtime to hello_world (AgentNameSchema forbids hyphens) - keep identifiers un-minified in builds so stack traces stay readable - rebuild scripts/build.ts on runWithExitCode; Bun.build already throws on failure so reportAndExit was dead code - rename Source to AssetSource; read() returns data, laziness moved to compose - add modeled ProjectFileExistsError - move src/project under src/core/project; test through FsProjectManager.create - extract config resolvers in the vended bin/cdk.ts and fail early on zero targets (typechecked against the built @aws/agentcore-cdk) * refactor(project): align scaffolding with codebase conventions Address review feedback: - AssetSource implementations are now classes (EmbeddedAssetSource, FsAssetSource) matching how interfaces are implemented elsewhere - TemplateSpec, Template, DirNode, and FileNode are types since they model concrete data, not extendable behavior - ProjectFileExistsError extends AgentCoreCLIError with a user error source so it participates in error classification and exit codes - atomicWrite moved from src/fs into the shared src/io module
1 parent ff00267 commit 8e2cecf

35 files changed

Lines changed: 1437 additions & 25 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3333

3434
# Finder (MacOS) folder config
3535
.DS_Store
36+
37+
.agentreview

.oxlintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"categories": {
55
"correctness": "error"
66
},
7-
"ignorePatterns": ["dist/", "node_modules/"],
7+
"ignorePatterns": ["dist/", "node_modules/", "src/assets/"],
88
"overrides": []
99
}

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ bun.lock
1010
# rewrites the recorded content (e.g. collapsing arrays) and breaks the exact
1111
# comparison the golden tests rely on. Refresh them with RECORD=1 instead.
1212
__fixtures__
13+
14+
*.snap
15+
src/assets

bun.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bunfig.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
# frames are plain text (no ANSI color codes) regardless of whether stdout is a
44
# TTY, keeping frame assertions deterministic across `bun test` and piped runs.
55
preload = ["./src/testing/setup.ts"]
6-
coveragePathIgnorePatterns = [
7-
"src/testing/**"
8-
]
6+
pathIgnorePatterns = ["src/assets/**"]
7+
coveragePathIgnorePatterns = ["src/testing/**", "src/assets/**"]

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
"dist"
1212
],
1313
"scripts": {
14-
"build": "bun build ./src/index.ts --target node --outdir ./dist --minify",
14+
"build": "bun scripts/build.ts bundle",
1515
"compile": "bun run compile:darwin-x64 && bun run compile:darwin-arm64 && bun run compile:linux-x64 && bun run compile:linux-arm64 && bun run compile:windows-x64 && bun run compile:windows-arm64",
16-
"compile:darwin-x64": "bun build --compile --minify --target=bun-darwin-x64 ./src/index.ts --outfile dist/bin/agentcore-darwin-x64",
17-
"compile:darwin-arm64": "bun build --compile --minify --target=bun-darwin-arm64 ./src/index.ts --outfile dist/bin/agentcore-darwin-arm64",
18-
"compile:linux-x64": "bun build --compile --minify --target=bun-linux-x64 ./src/index.ts --outfile dist/bin/agentcore-linux-x64",
19-
"compile:linux-arm64": "bun build --compile --minify --target=bun-linux-arm64 ./src/index.ts --outfile dist/bin/agentcore-linux-arm64",
20-
"compile:windows-x64": "bun build --compile --minify --target=bun-windows-x64 ./src/index.ts --outfile dist/bin/agentcore-windows-x64",
21-
"compile:windows-arm64": "bun build --compile --minify --target=bun-windows-arm64 ./src/index.ts --outfile dist/bin/agentcore-windows-arm64",
16+
"compile:darwin-x64": "bun scripts/build.ts compile bun-darwin-x64",
17+
"compile:darwin-arm64": "bun scripts/build.ts compile bun-darwin-arm64",
18+
"compile:linux-x64": "bun scripts/build.ts compile bun-linux-x64",
19+
"compile:linux-arm64": "bun scripts/build.ts compile bun-linux-arm64",
20+
"compile:windows-x64": "bun scripts/build.ts compile bun-windows-x64",
21+
"compile:windows-arm64": "bun scripts/build.ts compile bun-windows-arm64",
2222
"start": "bun run src/index.ts",
2323
"test": "bun test",
2424
"typecheck": "tsc --noEmit",
@@ -65,6 +65,7 @@
6565
"string-width": "^8.2.2",
6666
"winston": "^3.19.0",
6767
"winston-daily-rotate-file": "^5.0.0",
68+
"handlebars": "^4.7.9",
6869
"zod": "^4.4.3"
6970
}
7071
}

scripts/build.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env bun
2+
3+
import { $ } from "bun";
4+
import { join, resolve } from "node:path";
5+
import { runWithExitCode } from "../src/runnable";
6+
7+
const REPO_ROOT = resolve(import.meta.dir, "..");
8+
const ASSETS_DIR = join(REPO_ROOT, "src", "assets");
9+
const ENTRYPOINT = join(REPO_ROOT, "src", "index.ts");
10+
const DIST = join(REPO_ROOT, "dist");
11+
12+
const ASSET_NAMING = "agentcore-assets/[dir]/[name].[ext]";
13+
14+
// Shrink whitespace/syntax but keep identifiers: minified names make stack
15+
// traces unreadable and erase error names telemetry keys on.
16+
const MINIFY = { whitespace: true, syntax: true, identifiers: false } as const;
17+
18+
/** Absolute paths of every asset file. dot:true so hidden files (.prettierrc) are included. */
19+
function discoverAssets(): string[] {
20+
const files = [...new Bun.Glob("**/*").scanSync({ cwd: ASSETS_DIR, onlyFiles: true, dot: true })];
21+
return files.sort().map((relativePath) => join(ASSETS_DIR, relativePath));
22+
}
23+
24+
/** Force asset files through the file loader so template .ts/.js are embedded as bytes, not compiled. */
25+
function assetLoaderPlugin(): Bun.BunPlugin {
26+
return {
27+
name: "asset-file-loader",
28+
setup(build) {
29+
build.onLoad({ filter: /src[/\\]assets[/\\]/ }, async ({ path }) => ({
30+
contents: await Bun.file(path).bytes(),
31+
loader: "file",
32+
}));
33+
},
34+
};
35+
}
36+
37+
/** Fail loudly on a non-UTF-8 asset — the source reads every asset as text. */
38+
async function assertAssetsAreText(assets: string[]): Promise<void> {
39+
const decoder = new TextDecoder("utf-8", { fatal: true });
40+
for (const path of assets) {
41+
try {
42+
decoder.decode(await Bun.file(path).bytes());
43+
} catch {
44+
throw new Error(`Asset is not valid UTF-8: ${path}`);
45+
}
46+
}
47+
}
48+
49+
// Bun.build rejects with an AggregateError on failure (throw defaults to true),
50+
// so build errors propagate to runWithExitCode like any other.
51+
async function bundle(): Promise<void> {
52+
await Bun.build({
53+
entrypoints: [ENTRYPOINT],
54+
outdir: DIST,
55+
target: "node",
56+
minify: MINIFY,
57+
});
58+
59+
// Mirror assets beside the emitted module for resolveAssetsRoot().
60+
const distAssets = join(DIST, "assets");
61+
await $`rm -rf ${distAssets}`;
62+
await $`cp -R ${ASSETS_DIR} ${distAssets}`;
63+
console.log(`Bundled to ${join(DIST, "index.js")} with assets/`);
64+
}
65+
66+
async function compile(target: string): Promise<void> {
67+
const assets = discoverAssets();
68+
await assertAssetsAreText(assets);
69+
70+
const outfile = join(DIST, "bin", `agentcore-${target.replace(/^bun-/, "")}`);
71+
await $`mkdir -p ${join(DIST, "bin")}`;
72+
73+
await Bun.build({
74+
entrypoints: [ENTRYPOINT, ...assets],
75+
compile: { target: target as Bun.Build.CompileTarget, outfile },
76+
minify: MINIFY,
77+
root: REPO_ROOT,
78+
naming: { asset: ASSET_NAMING },
79+
plugins: [assetLoaderPlugin()],
80+
});
81+
console.log(`Compiled ${target}${outfile} (${assets.length} assets embedded)`);
82+
}
83+
84+
process.exit(
85+
await runWithExitCode(async () => {
86+
const [command, target] = process.argv.slice(2);
87+
88+
if (command === "bundle") {
89+
await bundle();
90+
} else if (command === "compile") {
91+
if (!target) {
92+
throw new Error("Usage: bun scripts/build.ts compile <bun-target>");
93+
}
94+
await compile(target);
95+
} else {
96+
throw new Error("Usage: bun scripts/build.ts <bundle|compile <target>>");
97+
}
98+
}),
99+
);

src/assets/cdk/.prettierrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"trailingComma": "es5",
3+
"printWidth": 120,
4+
"tabWidth": 2,
5+
"semi": true,
6+
"singleQuote": true,
7+
"arrowParens": "avoid"
8+
}

src/assets/cdk/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# AgentCore CDK Project
2+
3+
This CDK project is managed by the AgentCore CLI. It deploys your agent infrastructure into AWS using the `@aws/agentcore-cdk` L3 constructs.
4+
5+
## Structure
6+
7+
- `bin/cdk.ts` — Entry point. Reads project configuration from `agentcore/` and creates a stack per deployment target.
8+
- `lib/cdk-stack.ts` — Defines `AgentCoreStack`, which wraps the `AgentCoreApplication` L3 construct.
9+
- `test/cdk.test.ts` — Unit tests for stack synthesis.
10+
11+
## Useful commands
12+
13+
- `npm run build` compile TypeScript to JavaScript
14+
- `npm run test` run unit tests
15+
- `npx cdk synth` emit the synthesized CloudFormation template
16+
- `npx cdk deploy` deploy this stack to your default AWS account/region
17+
- `npx cdk diff` compare deployed stack with current state
18+
19+
## Usage
20+
21+
You typically don't need to interact with this directory directly. The AgentCore CLI handles synthesis and deployment:
22+
23+
<!-- TODO: revisit these commands once the project CLI surface is final —
24+
they may need a project prefix (e.g. --project / cwd) to disambiguate. -->
25+
26+
```bash
27+
agentcore deploy # synthesizes and deploys via CDK
28+
agentcore status # checks deployment status
29+
```

0 commit comments

Comments
 (0)