diff --git a/libs/template/builtin.go b/libs/template/builtin.go index 5b10534ef54..6a86fc5d545 100644 --- a/libs/template/builtin.go +++ b/libs/template/builtin.go @@ -8,6 +8,12 @@ import ( //go:embed all:templates var builtinTemplates embed.FS +// sharedLibraryDir holds definitions shared across templates; it has no schema, so builtin() excludes it. +const sharedLibraryDir = "common" + +// sharedLibraryFS is parsed into every template's namespace by newRenderer. +var sharedLibraryFS, _ = fs.Sub(builtinTemplates, "templates/"+sharedLibraryDir+"/"+libraryDirName) + // builtinTemplate represents a template that is built into the CLI. type builtinTemplate struct { Name string @@ -32,6 +38,11 @@ func builtin() ([]builtinTemplate, error) { continue } + // The shared library dir is not a template; skip it. + if entry.Name() == sharedLibraryDir { + continue + } + templateFS, err := fs.Sub(templates, entry.Name()) if err != nil { return nil, err diff --git a/libs/template/renderer.go b/libs/template/renderer.go index 94745da2b0b..63558b8523b 100644 --- a/libs/template/renderer.go +++ b/libs/template/renderer.go @@ -75,20 +75,16 @@ func newRenderer( // Initialize new template, with helper functions loaded tmpl := template.New("").Funcs(helpers) - // Find user-defined templates in the library directory - matches, err := fs.Glob(templateFS, path.Join(libraryDir, "*")) + // Parse the shared library before the template's own, so a same-named + // definition in the template's library takes precedence. + tmpl, err := parseLibrary(tmpl, sharedLibraryFS, "*") if err != nil { return nil, err } - // Parse user-defined templates. - // Note: we do not call [ParseFS] with the glob directly because - // it returns an error if no files match the pattern. - if len(matches) != 0 { - tmpl, err = tmpl.ParseFS(templateFS, matches...) - if err != nil { - return nil, err - } + tmpl, err = parseLibrary(tmpl, templateFS, path.Join(libraryDir, "*")) + if err != nil { + return nil, err } srcFS, err := fs.Sub(templateFS, path.Clean(templateDir)) @@ -109,6 +105,19 @@ func newRenderer( }, nil } +// parseLibrary parses files in fsys matching pattern into tmpl, tolerating no matches +// (unlike [template.Template.ParseFS], which errors when nothing matches). +func parseLibrary(tmpl *template.Template, fsys fs.FS, pattern string) (*template.Template, error) { + matches, err := fs.Glob(fsys, pattern) + if err != nil { + return nil, err + } + if len(matches) == 0 { + return tmpl, nil + } + return tmpl.ParseFS(fsys, matches...) +} + // Executes the template by applying config on it. Returns the materialized template // as a string func (r *renderer) executeTemplate(templateDefinition string) (string, error) { diff --git a/libs/template/renderer_test.go b/libs/template/renderer_test.go index bb839628627..837584c452a 100644 --- a/libs/template/renderer_test.go +++ b/libs/template/renderer_test.go @@ -155,6 +155,34 @@ func TestRendererWithAssociatedTemplateInLibrary(t *testing.T) { assert.Equal(t, "shreyas.goenka@databricks.com", strings.Trim(string(b), "\n\r")) } +func TestRendererSharedLibraryAndOverride(t *testing.T) { + tmpDir := t.TempDir() + + ctx := t.Context() + ctx = cmdctx.SetWorkspaceClient(ctx, nil) + helpers := loadHelpers(ctx) + r, err := newRenderer(ctx, nil, helpers, os.DirFS("."), "./testdata/library-override/template", "./testdata/library-override/library") + require.NoError(t, err) + + err = r.walk() + require.NoError(t, err) + out, err := filer.NewLocalClient(tmpDir) + require.NoError(t, err) + err = r.persistToDisk(ctx, out) + require.NoError(t, err) + + b, err := os.ReadFile(filepath.Join(tmpDir, "out")) + require.NoError(t, err) + got := string(b) + + // agents_md is defined only in the shared library, so rendering it (a heading) + // without error proves the shared library is parsed into the template's namespace. + assert.Contains(t, got, "shared: #") + // claude_md is defined in both the shared library and this template's own + // library; the template's own definition must take precedence. + assert.Contains(t, got, "own: OWN WINS") +} + func TestRendererExecuteTemplate(t *testing.T) { templateText := `"{{.count}} items are made of {{.Material}}". {{if eq .Animal "sheep" }} diff --git a/libs/template/templates/common/library/agents.tmpl b/libs/template/templates/common/library/agents.tmpl new file mode 100644 index 00000000000..7455ab15ab7 --- /dev/null +++ b/libs/template/templates/common/library/agents.tmpl @@ -0,0 +1,45 @@ +{{- /* + * Template for the AGENTS.md / CLAUDE.md files created in new projects. + * + * Goal for this file is to point agents to Databricks AI Tools for guidance. + * A secondary goal is to offer a placeholder to users to extend with their own instructions. + * It is a non-goal to inline all instructions. + * + */ -}} + +{{- define "agents_md" -}} +# Declarative Automation Bundles Project + +This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. + +## For AI Agents: Use Databricks AI Tools + +**BEFORE any other action, read the `databricks-core` skill.** + +It sets you up to work with this project reliably: CLI authentication, profile +selection, data discovery, and the bundle deployment workflow. Without it, +results are often slower and less accurate. + +If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: + +```bash +databricks aitools install +``` + +If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install + +--- + +## Project Instructions + + +{{- end -}} + +{{- define "claude_md" -}} +# CLAUDE.md + +Project guidance for AI agents lives in AGENTS.md. +Claude Code loads it via the import below. + +@AGENTS.md +{{- end -}} diff --git a/libs/template/templates/dbt-sql/template/{{.project_name}}/AGENTS.md.tmpl b/libs/template/templates/dbt-sql/template/{{.project_name}}/AGENTS.md.tmpl index 6243fbb1ad2..4fdb4bba90c 100644 --- a/libs/template/templates/dbt-sql/template/{{.project_name}}/AGENTS.md.tmpl +++ b/libs/template/templates/dbt-sql/template/{{.project_name}}/AGENTS.md.tmpl @@ -1,33 +1 @@ -{{- /* - * Template for the AGENTS.md / CLAUDE.md files created in new projects. - * - * Goal for this file is to point agents to Databricks AI Tools for guidance. - * A secondary goal is to offer a placeholder to users to extend with their own instructions. - * It is a non-goal to inline all instructions. - * - */ -}} -# Declarative Automation Bundles Project - -This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. - -## For AI Agents: Use Databricks AI Tools - -**BEFORE any other action, read the `databricks-core` skill.** - -It sets you up to work with this project reliably: CLI authentication, profile -selection, data discovery, and the bundle deployment workflow. Without it, -results are often slower and less accurate. - -If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: - -```bash -databricks aitools install -``` - -If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install - ---- - -## Project Instructions - - +{{template "agents_md" .}} diff --git a/libs/template/templates/dbt-sql/template/{{.project_name}}/CLAUDE.md.tmpl b/libs/template/templates/dbt-sql/template/{{.project_name}}/CLAUDE.md.tmpl index 5612c9bde2f..197fa48b81b 100644 --- a/libs/template/templates/dbt-sql/template/{{.project_name}}/CLAUDE.md.tmpl +++ b/libs/template/templates/dbt-sql/template/{{.project_name}}/CLAUDE.md.tmpl @@ -1,6 +1 @@ -# CLAUDE.md - -Project guidance for AI agents lives in AGENTS.md. -Claude Code loads it via the import below. - -@AGENTS.md +{{template "claude_md" .}} diff --git a/libs/template/templates/default-scala/template/{{.project_name}}/AGENTS.md.tmpl b/libs/template/templates/default-scala/template/{{.project_name}}/AGENTS.md.tmpl index 6243fbb1ad2..4fdb4bba90c 100644 --- a/libs/template/templates/default-scala/template/{{.project_name}}/AGENTS.md.tmpl +++ b/libs/template/templates/default-scala/template/{{.project_name}}/AGENTS.md.tmpl @@ -1,33 +1 @@ -{{- /* - * Template for the AGENTS.md / CLAUDE.md files created in new projects. - * - * Goal for this file is to point agents to Databricks AI Tools for guidance. - * A secondary goal is to offer a placeholder to users to extend with their own instructions. - * It is a non-goal to inline all instructions. - * - */ -}} -# Declarative Automation Bundles Project - -This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. - -## For AI Agents: Use Databricks AI Tools - -**BEFORE any other action, read the `databricks-core` skill.** - -It sets you up to work with this project reliably: CLI authentication, profile -selection, data discovery, and the bundle deployment workflow. Without it, -results are often slower and less accurate. - -If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: - -```bash -databricks aitools install -``` - -If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install - ---- - -## Project Instructions - - +{{template "agents_md" .}} diff --git a/libs/template/templates/default-scala/template/{{.project_name}}/CLAUDE.md.tmpl b/libs/template/templates/default-scala/template/{{.project_name}}/CLAUDE.md.tmpl index 5612c9bde2f..197fa48b81b 100644 --- a/libs/template/templates/default-scala/template/{{.project_name}}/CLAUDE.md.tmpl +++ b/libs/template/templates/default-scala/template/{{.project_name}}/CLAUDE.md.tmpl @@ -1,6 +1 @@ -# CLAUDE.md - -Project guidance for AI agents lives in AGENTS.md. -Claude Code loads it via the import below. - -@AGENTS.md +{{template "claude_md" .}} diff --git a/libs/template/templates/default-sql/template/{{.project_name}}/AGENTS.md.tmpl b/libs/template/templates/default-sql/template/{{.project_name}}/AGENTS.md.tmpl index 6243fbb1ad2..4fdb4bba90c 100644 --- a/libs/template/templates/default-sql/template/{{.project_name}}/AGENTS.md.tmpl +++ b/libs/template/templates/default-sql/template/{{.project_name}}/AGENTS.md.tmpl @@ -1,33 +1 @@ -{{- /* - * Template for the AGENTS.md / CLAUDE.md files created in new projects. - * - * Goal for this file is to point agents to Databricks AI Tools for guidance. - * A secondary goal is to offer a placeholder to users to extend with their own instructions. - * It is a non-goal to inline all instructions. - * - */ -}} -# Declarative Automation Bundles Project - -This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. - -## For AI Agents: Use Databricks AI Tools - -**BEFORE any other action, read the `databricks-core` skill.** - -It sets you up to work with this project reliably: CLI authentication, profile -selection, data discovery, and the bundle deployment workflow. Without it, -results are often slower and less accurate. - -If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: - -```bash -databricks aitools install -``` - -If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install - ---- - -## Project Instructions - - +{{template "agents_md" .}} diff --git a/libs/template/templates/default-sql/template/{{.project_name}}/CLAUDE.md.tmpl b/libs/template/templates/default-sql/template/{{.project_name}}/CLAUDE.md.tmpl index 5612c9bde2f..197fa48b81b 100644 --- a/libs/template/templates/default-sql/template/{{.project_name}}/CLAUDE.md.tmpl +++ b/libs/template/templates/default-sql/template/{{.project_name}}/CLAUDE.md.tmpl @@ -1,6 +1 @@ -# CLAUDE.md - -Project guidance for AI agents lives in AGENTS.md. -Claude Code loads it via the import below. - -@AGENTS.md +{{template "claude_md" .}} diff --git a/libs/template/templates/default/template/{{.project_name}}/AGENTS.md.tmpl b/libs/template/templates/default/template/{{.project_name}}/AGENTS.md.tmpl index 6243fbb1ad2..4fdb4bba90c 100644 --- a/libs/template/templates/default/template/{{.project_name}}/AGENTS.md.tmpl +++ b/libs/template/templates/default/template/{{.project_name}}/AGENTS.md.tmpl @@ -1,33 +1 @@ -{{- /* - * Template for the AGENTS.md / CLAUDE.md files created in new projects. - * - * Goal for this file is to point agents to Databricks AI Tools for guidance. - * A secondary goal is to offer a placeholder to users to extend with their own instructions. - * It is a non-goal to inline all instructions. - * - */ -}} -# Declarative Automation Bundles Project - -This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. - -## For AI Agents: Use Databricks AI Tools - -**BEFORE any other action, read the `databricks-core` skill.** - -It sets you up to work with this project reliably: CLI authentication, profile -selection, data discovery, and the bundle deployment workflow. Without it, -results are often slower and less accurate. - -If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: - -```bash -databricks aitools install -``` - -If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install - ---- - -## Project Instructions - - +{{template "agents_md" .}} diff --git a/libs/template/templates/default/template/{{.project_name}}/CLAUDE.md.tmpl b/libs/template/templates/default/template/{{.project_name}}/CLAUDE.md.tmpl index 5612c9bde2f..197fa48b81b 100644 --- a/libs/template/templates/default/template/{{.project_name}}/CLAUDE.md.tmpl +++ b/libs/template/templates/default/template/{{.project_name}}/CLAUDE.md.tmpl @@ -1,6 +1 @@ -# CLAUDE.md - -Project guidance for AI agents lives in AGENTS.md. -Claude Code loads it via the import below. - -@AGENTS.md +{{template "claude_md" .}} diff --git a/libs/template/testdata/library-override/library/override.tmpl b/libs/template/testdata/library-override/library/override.tmpl new file mode 100644 index 00000000000..aeb54aea7f0 --- /dev/null +++ b/libs/template/testdata/library-override/library/override.tmpl @@ -0,0 +1 @@ +{{define "claude_md"}}OWN WINS{{end}} diff --git a/libs/template/testdata/library-override/template/out.tmpl b/libs/template/testdata/library-override/template/out.tmpl new file mode 100644 index 00000000000..8da557e7af8 --- /dev/null +++ b/libs/template/testdata/library-override/template/out.tmpl @@ -0,0 +1,2 @@ +own: {{template "claude_md" .}} +shared: {{template "agents_md" .}}