diff --git a/pkg/cli/add_package_manifest.go b/pkg/cli/add_package_manifest.go index 525b76b13c4..905382aaf8d 100644 --- a/pkg/cli/add_package_manifest.go +++ b/pkg/cli/add_package_manifest.go @@ -45,6 +45,7 @@ type resolvedRepositoryPackage struct { Name string Emoji string Description string + License string DocsPath string InstallationSource []string SkillFiles []resolvedPackageSkillFile @@ -159,6 +160,7 @@ func resolveRepositoryPackage(repoSpec *RepoSpec, host string) (*resolvedReposit Name: manifest.Name, Emoji: manifest.Emoji, Description: manifest.Description, + License: manifest.License, DocsPath: docsPath, InstallationSource: installationSources, SkillFiles: skillFiles, @@ -191,6 +193,7 @@ type repositoryPackageManifest struct { Name string Emoji string Description string + License string Includes []string Files []string Skills []string // skill directory paths (e.g. "skills/my-skill") @@ -255,6 +258,10 @@ func parseRepositoryPackageManifest(manifestPath string, content []byte) (*repos manifest.Emoji = emoji } + if license, ok := stringValue(root["license"]); ok { + manifest.License = license + } + if includesValue, ok := root["includes"]; ok { includes, includeWarnings := extractManifestIncludes(includesValue, manifestPath) manifest.Includes = includes diff --git a/pkg/cli/add_package_manifest_test.go b/pkg/cli/add_package_manifest_test.go index 7b8bfd2eb31..322fa646ce2 100644 --- a/pkg/cli/add_package_manifest_test.go +++ b/pkg/cli/add_package_manifest_test.go @@ -56,6 +56,7 @@ func TestResolveRepositoryPackage(t *testing.T) { return []byte(`name: Repo Assist emoji: 🤖 description: Friendly repository automation +license: MIT files: - workflows/review.md - .github/workflows/nightly-review.md @@ -77,6 +78,7 @@ files: assert.Equal(t, "aw.yml", pkg.ManifestPath) assert.Equal(t, "Repo Assist", pkg.Name) assert.Equal(t, "🤖", pkg.Emoji) + assert.Equal(t, "MIT", pkg.License) assert.Equal(t, "README.md", pkg.DocsPath) assert.Equal(t, []string{"workflows/review.md", ".github/workflows/nightly-review.md"}, pkg.InstallationSource) require.NotEmpty(t, pkg.Warnings) @@ -421,6 +423,22 @@ emoji: assert.Contains(t, err.Error(), `emoji`) }) + t.Run("rejects non-string license field", func(t *testing.T) { + downloadPackageFileFromGitHubForHost = func(owner, repo, path, ref, host string) ([]byte, error) { + if path == "aw.yml" { + return []byte(`name: Repo Assist +license: + id: MIT +`), nil + } + return nil, createRepositoryPackageNotFoundError(path) + } + + _, err := resolveRepositoryPackage(&RepoSpec{RepoSlug: "owner/repo"}, "") + require.Error(t, err) + assert.Contains(t, err.Error(), `license`) + }) + t.Run("rejects incompatible min-version", func(t *testing.T) { downloadPackageFileFromGitHubForHost = func(owner, repo, path, ref, host string) ([]byte, error) { if path == "aw.yml" { diff --git a/pkg/parser/schemas/aw_manifest_schema.json b/pkg/parser/schemas/aw_manifest_schema.json index 17cf8b01883..c4b3dd35050 100644 --- a/pkg/parser/schemas/aw_manifest_schema.json +++ b/pkg/parser/schemas/aw_manifest_schema.json @@ -23,6 +23,9 @@ "description": { "type": "string" }, + "license": { + "type": "string" + }, "includes": { "type": "array", "description": "Installable package entries. Use folder naming conventions to infer type: workflows under workflows/, agentic-workflows/, or .github/workflows/; skill directories under skills/ or .github/skills/; agent files under agents/ or .github/agents/.",