Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 51 additions & 314 deletions internal/skills/generator.go

Large diffs are not rendered by default.

110 changes: 53 additions & 57 deletions internal/skills/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ func init() {
schema.Register(schema.CommandSchema{
Command: "testsearch",
Description: "Test search description.",
WhenToUse: "Find test things.",
Surface: schema.SurfacePlatform,
Flags: map[string]schema.FlagSchema{
"--query": {Type: "string", Description: "Search query", Required: true},
"--page": {Type: "integer", Description: "Page number", Default: 1},
},
Example: "glean testsearch --query hello",
})
schema.Register(schema.CommandSchema{
Command: "testpins",
Description: "Test pins description.",
Surface: schema.SurfaceLegacy,
Flags: map[string]schema.FlagSchema{},
Example: "glean testpins list",
})
Expand All @@ -38,17 +40,11 @@ func runGenerator(t *testing.T) string {
return dir
}

// registeredCommands returns the list of command names that Generate would
// process (registry minus skipCommands).
func registeredCommands() []string {
var out []string
for _, name := range schema.List() {
if skipCommands[name] {
continue
}
out = append(out, name)
}
return out
func readRootSkill(t *testing.T, dir string) string {
t.Helper()
body, err := os.ReadFile(filepath.Join(dir, rootSkillName, "SKILL.md"))
require.NoError(t, err)
return string(body)
}

func TestGenerate_WritesRootSkill(t *testing.T) {
Expand All @@ -60,14 +56,41 @@ func TestGenerate_WritesRootSkill(t *testing.T) {
assert.Greater(t, info.Size(), int64(0), "root SKILL.md must be non-empty")
}

func TestGenerate_WritesReferenceForEveryCommand(t *testing.T) {
func TestGenerate_PointsToAgentHelpAsSourceOfTruth(t *testing.T) {
content := readRootSkill(t, runGenerator(t))

assert.Contains(t, content, "glean agent-help")
assert.Contains(t, strings.ToLower(content), "source of truth")
assert.Contains(t, content, "--json")
}

func TestGenerate_CommandMapFromRegistry(t *testing.T) {
content := readRootSkill(t, runGenerator(t))

assert.Contains(t, content, "`glean testsearch`")
assert.Contains(t, content, "Find test things.")
assert.Contains(t, content, schema.SurfacePlatform)
// testpins has no WhenToUse — Description is the fallback.
assert.Contains(t, content, "Test pins description.")
}

func TestGenerate_NoReferenceFiles(t *testing.T) {
dir := runGenerator(t)

for _, name := range registeredCommands() {
p := filepath.Join(dir, rootSkillName, "reference", name+".md")
_, err := os.Stat(p)
assert.NoError(t, err, "expected reference file for command %q at %s", name, p)
}
_, err := os.Stat(filepath.Join(dir, rootSkillName, "reference"))
assert.True(t, os.IsNotExist(err), "per-command reference files are retired in favor of agent-help")
}

func TestGenerate_RemovesRetiredReferenceDir(t *testing.T) {
dir := t.TempDir()
stale := filepath.Join(dir, rootSkillName, "reference")
require.NoError(t, os.MkdirAll(stale, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(stale, "search.md"), []byte("stale"), 0o644))

require.NoError(t, Generate(dir))

_, err := os.Stat(stale)
assert.True(t, os.IsNotExist(err), "retired reference dir must be cleaned up")
}

func TestGenerate_NoLegacySkillDirs(t *testing.T) {
Expand Down Expand Up @@ -140,46 +163,9 @@ func TestCleanStaleSkillDirs_PreservesUnrelatedDirs(t *testing.T) {
assert.NoError(t, err, "unrelated dir must not be touched by cleanup")
}

func TestReferenceFile_NoFrontmatter(t *testing.T) {
dir := runGenerator(t)

cmds := registeredCommands()
require.NotEmpty(t, cmds, "at least one registered command required for this test")

p := filepath.Join(dir, rootSkillName, "reference", cmds[0]+".md")
body, err := os.ReadFile(p)
require.NoError(t, err)
content := string(body)

assert.False(t, strings.HasPrefix(content, "---"),
"reference file must not start with YAML frontmatter: %s", p)
assert.NotContains(t, content, "PREREQUISITE",
"reference file must not carry the legacy prereq line")
}

func TestRootSkill_LinksPointToReferenceFiles(t *testing.T) {
dir := runGenerator(t)

body, err := os.ReadFile(filepath.Join(dir, rootSkillName, "SKILL.md"))
require.NoError(t, err)
content := string(body)

for _, name := range registeredCommands() {
expected := "(reference/" + name + ".md)"
assert.Contains(t, content, expected,
"root SKILL.md must link to %s", expected)
}

assert.NotContains(t, content, "../"+skillPrefix,
"root SKILL.md must not contain legacy ../glean-cli-<cmd>/ links")
}

func TestRootSkill_IncludesMigrationNote(t *testing.T) {
dir := runGenerator(t)

body, err := os.ReadFile(filepath.Join(dir, rootSkillName, "SKILL.md"))
require.NoError(t, err)
assert.Contains(t, string(body), "npx -y skills remove",
content := readRootSkill(t, runGenerator(t))
assert.Contains(t, content, "npx -y skills remove",
"root SKILL.md must carry the per-command cleanup one-liner")
}

Expand Down Expand Up @@ -209,3 +195,13 @@ func snapshotTree(t *testing.T, root string) map[string]string {
require.NoError(t, err)
return out
}

// Verify the retired strings module-wide: the generator must no longer strip
// frontmatter into reference files or reference glean schema.
func TestRootSkill_NoStaleSchemaReferences(t *testing.T) {
content := readRootSkill(t, runGenerator(t))
assert.False(t, strings.Contains(content, "glean schema "),
"skill must route agents to agent-help, not the deprecated schema command")
assert.NotContains(t, content, "(reference/",
"skill must not link to retired reference files")
}
116 changes: 42 additions & 74 deletions skills/glean-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,100 +6,68 @@ compatibility: Requires the glean binary on $PATH. Install via brew install glea

# Glean CLI

The `glean` command-line tool provides authenticated access to your company's Glean instance. It can search documents, chat with Glean Assistant, look up people and teams, manage collections, and more.
The `glean` command-line tool provides authenticated access to your company's Glean instance.

## When to Use
## Source of truth: ask the binary, not this file

Use the Glean CLI when the user:

- Asks about internal documents, policies, wikis, or company knowledge
- Wants to find people, teams, or org structure
- Needs to search across enterprise data sources (Confluence, Jira, Google Drive, Slack, etc.)
- Asks questions that require company-specific context
- Wants to manage Glean resources (collections, shortcuts, pins, announcements)

## Installation
This skill is intentionally thin. The CLI describes itself — always start with:

```bash
brew install gleanwork/tap/glean-cli
glean agent-help # environment context + command map with when-to-use guidance
glean agent-help <command> [sub] # exact flags, payload shapes, examples for one command
glean agent-help <command> --json # machine-readable
```

The output is generated from the installed binary, so it matches the version
on this machine exactly. **When anything below (or in any other document)
disagrees with `glean agent-help`, trust `glean agent-help`.**
It also reports whether the environment is authenticated and which API surface
(platform vs legacy) is active.

## Authentication

```bash
# Browser-based OAuth (interactive — recommended)
glean auth login
glean auth login # browser-based OAuth (interactive)
glean auth status # verify credentials

# Verify credentials
glean auth status

# CI/scripting (no interactive setup needed)
# CI/scripting
export GLEAN_API_TOKEN=your-token
export GLEAN_SERVER_URL=<your Glean server URL>
```

Credentials resolve in this order: environment variables → system keyring → ~/.glean/config.json.

## CLI Syntax

```bash
glean <command> [subcommand] [flags]
```

### Global Flags

| Flag | Description |
|------|-------------|
| --output <FORMAT> | json (default), ndjson (one result per line), text |
| --fields <PATHS> | Dot-path field projection (e.g. results.document.title,results.document.url) |
| --json <PAYLOAD> | Complete JSON request body (overrides all other flags) |
| --dry-run | Print request body without sending |

## Schema Introspection

Always call `glean schema <command>` before invoking a command you haven't used before.

```bash
glean schema | jq '.commands' # list all commands
glean schema search | jq '.flags' # flags for search
```

## Security Rules
## Command map

| Command | When to use | API |
|---------|-------------|-----|
| `glean activity` | Report page-view activity events or submit result feedback to improve ranking. | legacy |
| `glean agent-help` | Start every session here: discover available commands, exact flags, payload shapes, and whether the environment is authenticated — without static docs in context. | local |
| `glean agents` | Discover and execute Glean agents (AI workflows). Use schemas first to learn an agent's expected input, then run. | platform |
| `glean announcements` | Publish or manage company announcements surfaced in Glean. | legacy |
| `glean answers` | Manage curated Q&A answer cards shown for matching queries. | legacy |
| `glean api` | Escape hatch: call any Glean API endpoint directly when no dedicated subcommand exists. | raw |
| `glean chat` | Ask Glean AI a question that needs a synthesized, cited answer reasoned over company knowledge, rather than a raw list of results. | legacy |
| `glean collections` | Curate sets of documents into named collections. | legacy |
| `glean documents` | Fetch full metadata, permissions, or an AI summary for documents you already identified (e.g. from search results). | legacy |
| `glean entities` | Look up people (org info, contact details) or other structured entities. | legacy |
| `glean insights` | Retrieve aggregate usage analytics (search/AI adoption metrics) for the deployment. | legacy |
| `glean messages` | Fetch a specific chat/communication message by ID. | legacy |
| `glean pins` | Pin a document to a search query so it always surfaces for that query. | legacy |
| `glean search` | Find documents, messages, and content across all connected datasources. Start here for any 'find X' task. | platform |
| `glean shortcuts` | Create or resolve go-links (short memorable URLs like go/roadmap). | legacy |
| `glean tools` | Discover and execute Glean tools (actions) such as creating tickets or sending messages. | legacy |
| `glean verification` | Track and update document freshness verification (verify docs, send reminders). | legacy |

(Regenerate this table's live equivalent anytime with `glean agent-help`.)

## Ground rules

- **Never** output API tokens or secrets directly
- **Always** use --dry-run before write/delete operations in automated pipelines
- Prefer environment variables over config files for CI/CD

## Error Handling

All errors go to stderr; stdout contains only structured output.
Exit code 0 = success, non-zero = error.

## Available Commands

| Command | Description |
|---------|-------------|
| [glean activity](reference/activity.md) | Report user activity and feedback. Subcommands: report, feedback. |
| [glean agents](reference/agents.md) | Manage and run Glean agents. Subcommands: list, get, schemas, run. |
| [glean announcements](reference/announcements.md) | Manage Glean announcements. Subcommands: create, update, delete. |
| [glean answers](reference/answers.md) | Manage Glean answers. Subcommands: list, get, create, update, delete. |
| [glean api](reference/api.md) | Make a raw authenticated HTTP request to any Glean REST API endpoint. |
| [glean chat](reference/chat.md) | Have a conversation with Glean AI. Streams response to stdout. |
| [glean collections](reference/collections.md) | Manage Glean collections. Subcommands: create, delete, update, add-items, delete-item. |
| [glean documents](reference/documents.md) | Retrieve and summarize Glean documents. Subcommands: get, get-by-facets, get-permissions, summarize. |
| [glean entities](reference/entities.md) | List and read Glean entities and people. Subcommands: list, read-people. |
| [glean insights](reference/insights.md) | Retrieve Glean usage insights. Subcommands: get. |
| [glean messages](reference/messages.md) | Retrieve Glean messages. Subcommands: get. |
| [glean pins](reference/pins.md) | Manage Glean pins. Subcommands: list, get, create, update, remove. |
| [glean search](reference/search.md) | Search for content in your Glean instance. Results are JSON. |
| [glean shortcuts](reference/shortcuts.md) | Manage Glean shortcuts (go-links). Subcommands: list, get, create, update, delete. |
| [glean tools](reference/tools.md) | List and run Glean tools. Subcommands: list, run. |
| [glean verification](reference/verification.md) | Manage document verification. Subcommands: list, verify, remind. |

- All errors go to stderr; stdout contains only structured output. Exit code 0 = success.

## Previously installed per-command skills?

Earlier versions of this project shipped one skill per command (`glean-cli-search`, `glean-cli-pins`, etc.). Those are superseded by this consolidated skill. If you still have them installed, remove them with:
Earlier versions of this project shipped one skill per command (`glean-cli-search`, `glean-cli-pins`, etc.) and static per-command reference files. Those are superseded by `glean agent-help`. If you still have the old skills installed, remove them with:

```bash
npx -y skills remove -g -y \
Expand Down
37 changes: 0 additions & 37 deletions skills/glean-cli/reference/activity.md

This file was deleted.

41 changes: 0 additions & 41 deletions skills/glean-cli/reference/agents.md

This file was deleted.

Loading