@sous-io/sous 0.1.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/LICENSE +201 -0
- package/README.md +154 -0
- package/bin/run.js +17 -0
- package/bin/xcv +5 -0
- package/package.json +81 -0
- package/shared-prompts/_partials/resume-task.md +51 -0
- package/shared-prompts/_partials/sub-agent-delegation.md +32 -0
- package/shared-prompts/_partials/update-task-file.md +52 -0
- package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +52 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +102 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +81 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +126 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +92 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +61 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +65 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +96 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +104 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +243 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +148 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +383 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +267 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +56 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +169 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +59 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +25 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +140 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +140 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +1 -0
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +185 -0
- package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +52 -0
- package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +59 -0
- package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +47 -0
- package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +26 -0
- package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +58 -0
- package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +27 -0
- package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +34 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/SKILL.tpl.md +177 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/examples/about-something.md +45 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/examples/do-something.md +33 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/references/advanced-patterns.md +87 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/references/commands.md +46 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/references/frontmatter.md +25 -0
- package/shared-prompts/skills/sous-skills/about-agent-skills/references/substitutions.md +50 -0
- package/shared-prompts/skills/sous-skills/about-liquid-templates/SKILL.tpl.md +268 -0
- package/shared-prompts/skills/sous-skills/about-liquid-templates/references/liquid-filters.md +82 -0
- package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +51 -0
- package/shared-prompts/skills/sous-skills/create-skill/SKILL.tpl.md +114 -0
- package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +122 -0
- package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +80 -0
- package/shared-prompts/skills/task-files/go/SKILL.tpl.md +14 -0
- package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +13 -0
- package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +93 -0
- package/shared-prompts/skills/task-files/update/SKILL.tpl.md +14 -0
- package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +13 -0
- package/src/base-command.ts +163 -0
- package/src/commands/build.ts +196 -0
- package/src/commands/clear.ts +71 -0
- package/src/commands/compile.ts +95 -0
- package/src/commands/launch.ts +111 -0
- package/src/commands/prune.ts +48 -0
- package/src/lib/build-service.ts +258 -0
- package/src/lib/config-discovery.ts +199 -0
- package/src/lib/env-local.ts +195 -0
- package/src/lib/include-resolver.ts +146 -0
- package/src/lib/markdown-compiler.ts +580 -0
- package/src/lib/pid-service.ts +88 -0
- package/src/lib/settings.ts +695 -0
- package/src/lib/state.ts +135 -0
- package/src/lib/watch-service.ts +115 -0
- package/src/templating/filters/bullet-list.ts +9 -0
- package/src/templating/filters/index.ts +8 -0
- package/src/templating/init-liquid-engine.ts +82 -0
- package/src/templating/lib/glob-files.ts +74 -0
- package/src/templating/lib/import-export.ts +32 -0
- package/src/templating/lib/tag-args.ts +19 -0
- package/src/templating/tags/exportScalarVarsJs.ts +43 -0
- package/src/templating/tags/getFiles.ts +89 -0
- package/src/templating/tags/index.ts +14 -0
- package/src/templating/tags/listFiles.ts +54 -0
- package/src/templating/tags/showVars.ts +22 -0
- package/src/utils/formatting.ts +338 -0
- package/src/utils/prompts.ts +19 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Example: Topical Skill (`about-*`)
|
|
2
|
+
|
|
3
|
+
```markdown
|
|
4
|
+
---
|
|
5
|
+
name: about-deployments
|
|
6
|
+
description: >
|
|
7
|
+
YOU MUST load this skill when working with deployments, deployment config, or when
|
|
8
|
+
the user mentions "deploy". Covers what a deployment is, how they work in this
|
|
9
|
+
project, and where deployment configuration lives.
|
|
10
|
+
user-invocable: false
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Abstract
|
|
14
|
+
|
|
15
|
+
This "topical skill" provides information about a specific concept: deployments.
|
|
16
|
+
|
|
17
|
+
## About Deployments
|
|
18
|
+
|
|
19
|
+
A deployment is the process of publishing a built application to a target environment.
|
|
20
|
+
In this project, deployments are managed via the `deploy/` directory and driven by
|
|
21
|
+
scripts in `deploy/scripts/`.
|
|
22
|
+
|
|
23
|
+
Deployments target three environments: `staging`, `production`, and `preview`. Each
|
|
24
|
+
has its own configuration file under `deploy/config/`.
|
|
25
|
+
|
|
26
|
+
## Reference Files
|
|
27
|
+
|
|
28
|
+
- [references/environments.md](references/environments.md) — per-environment config
|
|
29
|
+
options, required env vars, and access requirements
|
|
30
|
+
- [references/rollback.md](references/rollback.md) — rollback procedures and known
|
|
31
|
+
failure modes
|
|
32
|
+
|
|
33
|
+
# Other Skills
|
|
34
|
+
|
|
35
|
+
## Action Skills
|
|
36
|
+
|
|
37
|
+
Action skills for deployments will have "deploy" in their name. Find the appropriate
|
|
38
|
+
action skill for what you want to do. If no appropriate action skill exists, ask the
|
|
39
|
+
user whether one should be created before continuing.
|
|
40
|
+
|
|
41
|
+
## Related Skills
|
|
42
|
+
|
|
43
|
+
You MUST load `about-infrastructure` for network and hosting context relevant to
|
|
44
|
+
deployments.
|
|
45
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Example: Action Skill (Command)
|
|
2
|
+
|
|
3
|
+
```markdown
|
|
4
|
+
---
|
|
5
|
+
name: deploy
|
|
6
|
+
description: >
|
|
7
|
+
YOU MUST use this skill when deploying the application. Do not use for rollbacks —
|
|
8
|
+
those follow a different process.
|
|
9
|
+
disable-model-invocation: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Abstract
|
|
13
|
+
|
|
14
|
+
This "action skill" (command) performs a specific operation: deploying the application
|
|
15
|
+
to a target environment.
|
|
16
|
+
|
|
17
|
+
A deployment publishes the built application to a hosting environment. This project
|
|
18
|
+
supports three environments: `staging`, `production`, and `preview`.
|
|
19
|
+
|
|
20
|
+
## Deploying
|
|
21
|
+
|
|
22
|
+
The agent performing this work MUST load `about-deployments`. Then run the deploy
|
|
23
|
+
script for the target environment:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bash ${CLAUDE_SKILL_DIR}/../../deploy/scripts/deploy.sh $ARGUMENTS
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
# Related Skills
|
|
30
|
+
|
|
31
|
+
You MUST load `about-deployments` for environment config, required env vars, and
|
|
32
|
+
access requirements.
|
|
33
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Advanced Patterns
|
|
2
|
+
|
|
3
|
+
## Dynamic Context Injection
|
|
4
|
+
|
|
5
|
+
The `` !`command` `` syntax runs a shell command before skill content is sent to Claude.
|
|
6
|
+
The output replaces the placeholder — Claude receives the rendered result, not the command.
|
|
7
|
+
|
|
8
|
+
```yaml
|
|
9
|
+
---
|
|
10
|
+
name: pr-summary
|
|
11
|
+
context: fork
|
|
12
|
+
agent: Explore
|
|
13
|
+
allowed-tools: Bash(gh *)
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Pull request context
|
|
17
|
+
- Diff: !`gh pr diff`
|
|
18
|
+
- Comments: !`gh pr view --comments`
|
|
19
|
+
- Changed files: !`gh pr diff --name-only`
|
|
20
|
+
|
|
21
|
+
## Task
|
|
22
|
+
Summarize this pull request...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Execution order: each `` !`command` `` runs first, output is inserted, then Claude sees
|
|
26
|
+
the fully-rendered prompt. This is preprocessing, not something Claude executes.
|
|
27
|
+
|
|
28
|
+
## Subagent Execution (`context: fork`)
|
|
29
|
+
|
|
30
|
+
Add `context: fork` to run the skill in an isolated subagent. The skill content becomes
|
|
31
|
+
the subagent's prompt. It does not have access to the current conversation history.
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
---
|
|
35
|
+
name: deep-research
|
|
36
|
+
context: fork
|
|
37
|
+
agent: Explore
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
Research $ARGUMENTS thoroughly:
|
|
41
|
+
1. Find relevant files using Glob and Grep
|
|
42
|
+
2. Read and analyze the code
|
|
43
|
+
3. Summarize findings with specific file references
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The `agent` field selects the subagent configuration:
|
|
47
|
+
- `Explore` — read-only tools optimized for codebase exploration
|
|
48
|
+
- `Plan` — planning-oriented execution
|
|
49
|
+
- `general-purpose` — default; full tool access
|
|
50
|
+
- Any custom agent defined in `.claude/agents/`
|
|
51
|
+
|
|
52
|
+
Results are summarized and returned to the main conversation.
|
|
53
|
+
|
|
54
|
+
> Only use `context: fork` for skills with explicit task instructions. Skills containing
|
|
55
|
+
> only reference guidelines (e.g. "use these conventions") will return without meaningful
|
|
56
|
+
> output — the subagent has no actionable task.
|
|
57
|
+
|
|
58
|
+
## Restricting Tool Access (`allowed-tools`)
|
|
59
|
+
|
|
60
|
+
Limit which tools Claude can use when a skill is active. Granted without per-use approval.
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
---
|
|
64
|
+
name: safe-reader
|
|
65
|
+
allowed-tools: Read, Grep, Glob
|
|
66
|
+
---
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Syntax supports wildcards: `Bash(gh *)` permits only `gh` subcommands via Bash.
|
|
70
|
+
|
|
71
|
+
## Supporting Files
|
|
72
|
+
|
|
73
|
+
Keep `SKILL.md` under ~500 lines. Move detailed reference material to separate files
|
|
74
|
+
and reference them from the skill body so Claude knows when to load them.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
my-skill/
|
|
78
|
+
├── SKILL.md # Overview, navigation, fundamental instructions
|
|
79
|
+
├── references/ # Deep-dive docs — loaded when needed, not always
|
|
80
|
+
│ └── api-spec.md
|
|
81
|
+
├── examples/ # Example outputs showing expected format
|
|
82
|
+
└── scripts/ # Executable scripts; referenced via $CLAUDE_SKILL_DIR
|
|
83
|
+
└── validate.sh
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Scripts are executed, not read into context. Reference them in `SKILL.md` with the
|
|
87
|
+
full path using `${CLAUDE_SKILL_DIR}/scripts/validate.sh`.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Commands Reference
|
|
2
|
+
|
|
3
|
+
A **command** is a user-invocable action skill that represents an intentional,
|
|
4
|
+
user-initiated workflow. The user explicitly triggers it with `/skill-name`.
|
|
5
|
+
|
|
6
|
+
## Key Properties
|
|
7
|
+
|
|
8
|
+
- Set `disable-model-invocation: true`. This removes the skill from Claude's context
|
|
9
|
+
entirely — it loads only when the user invokes it. See `frontmatter.md` for the
|
|
10
|
+
invocation matrix.
|
|
11
|
+
- Because the model never sees the description, write it for humans, not for Claude.
|
|
12
|
+
It appears in the `/` autocomplete menu. Plain, informative language is appropriate —
|
|
13
|
+
strong trigger language ("YOU MUST") is unnecessary and out of place.
|
|
14
|
+
- Commands do not require an `# Abstract` section. Include one only if useful context
|
|
15
|
+
is genuinely needed. Headings are optional in general — use them only if the content
|
|
16
|
+
is complex enough to warrant structure.
|
|
17
|
+
|
|
18
|
+
## Arguments
|
|
19
|
+
|
|
20
|
+
Commands often accept arguments (e.g. `/deploy staging`). When they do:
|
|
21
|
+
|
|
22
|
+
1. Use `$ARGUMENTS` (or `$0`, `$1`, etc.) in the skill body where the arguments should
|
|
23
|
+
be substituted. See `substitutions.md` for full syntax.
|
|
24
|
+
2. Add an `argument-hint` field to frontmatter — it appears in autocomplete next to the
|
|
25
|
+
command name.
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
---
|
|
29
|
+
name: deploy
|
|
30
|
+
description: Deploy the application to a target environment.
|
|
31
|
+
argument-hint: "[environment]"
|
|
32
|
+
disable-model-invocation: true
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
Deploy to the $0 environment.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If `$ARGUMENTS` is not present in the body but arguments are passed, Claude Code
|
|
39
|
+
appends them automatically as `ARGUMENTS: <value>`.
|
|
40
|
+
|
|
41
|
+
## When to Omit `disable-model-invocation`
|
|
42
|
+
|
|
43
|
+
Not every command needs `disable-model-invocation: true`. If it makes sense for Claude
|
|
44
|
+
to invoke the command autonomously on the user's behalf (e.g. a lightweight helper with
|
|
45
|
+
no side effects), omit the flag. Use judgement — the defining question is whether the
|
|
46
|
+
action should require explicit user intent.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Frontmatter Reference
|
|
2
|
+
|
|
3
|
+
All fields are optional, though `description` is strongly recommended.
|
|
4
|
+
`name` defaults to the directory name if omitted.
|
|
5
|
+
|
|
6
|
+
| Field | Description |
|
|
7
|
+
|:---------------------------|:------------|
|
|
8
|
+
| `name` | Slash-command name. Lowercase letters, numbers, hyphens (max 64 chars). Defaults to directory name. |
|
|
9
|
+
| `description` | When to invoke this skill. Used by Claude for auto-invocation. Falls back to first paragraph of content. |
|
|
10
|
+
| `argument-hint` | Hint shown in autocomplete. Example: `[issue-number]` or `[filename] [format]`. |
|
|
11
|
+
| `disable-model-invocation` | `true` — user-only invocation. Description removed from Claude's context entirely. Use for side-effect workflows. Default: `false`. |
|
|
12
|
+
| `user-invocable` | `false` — hides from `/` menu; only Claude can invoke. Use for background knowledge. Default: `true`. |
|
|
13
|
+
| `allowed-tools` | Tools Claude may use without per-use approval when this skill is active. Example: `Read, Grep, Glob`. |
|
|
14
|
+
| `model` | Model to use when this skill is active. |
|
|
15
|
+
| `context` | `fork` — run the skill in an isolated subagent. Skill content becomes the subagent prompt. |
|
|
16
|
+
| `agent` | Subagent type when `context: fork` is set. Options: `Explore`, `Plan`, `general-purpose`, or any custom agent in `.claude/agents/`. Defaults to `general-purpose`. |
|
|
17
|
+
| `hooks` | Hooks scoped to this skill's lifecycle. |
|
|
18
|
+
|
|
19
|
+
## Invocation Matrix
|
|
20
|
+
|
|
21
|
+
| Frontmatter | User can invoke | Claude can invoke | When loaded into context |
|
|
22
|
+
|:---------------------------------|:----------------|:------------------|:--------------------------------------------------------------|
|
|
23
|
+
| (default) | Yes | Yes | Description always in context; full skill loads on invocation |
|
|
24
|
+
| `disable-model-invocation: true` | Yes | No | Not in context at all; loads only when user invokes |
|
|
25
|
+
| `user-invocable: false` | No | Yes | Description always in context; full skill loads on invocation |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# String Substitutions
|
|
2
|
+
|
|
3
|
+
Skills support dynamic substitution in the skill body content.
|
|
4
|
+
|
|
5
|
+
## Argument Substitutions
|
|
6
|
+
|
|
7
|
+
| Variable | Description |
|
|
8
|
+
|:----------------|:------------|
|
|
9
|
+
| `$ARGUMENTS` | All arguments passed when invoking the skill. If not present in content, arguments are appended as `ARGUMENTS: <value>`. |
|
|
10
|
+
| `$ARGUMENTS[N]` | A specific argument by 0-based index. `$ARGUMENTS[0]` is the first argument. |
|
|
11
|
+
| `$N` | Shorthand for `$ARGUMENTS[N]`. `$0` = first argument, `$1` = second, etc. |
|
|
12
|
+
|
|
13
|
+
### Example
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
---
|
|
17
|
+
name: migrate-component
|
|
18
|
+
description: Migrate a component from one framework to another
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Migrate the $0 component from $1 to $2.
|
|
22
|
+
Preserve all existing behavior and tests.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Running `/migrate-component SearchBar React Vue` substitutes `SearchBar`, `React`, `Vue`.
|
|
26
|
+
|
|
27
|
+
If arguments are passed but `$ARGUMENTS` (or `$N`) is not present in the content,
|
|
28
|
+
Claude Code appends `ARGUMENTS: <value>` to the end of the skill content automatically.
|
|
29
|
+
|
|
30
|
+
## Session and Path Substitutions
|
|
31
|
+
|
|
32
|
+
| Variable | Description |
|
|
33
|
+
|:-----------------------|:------------|
|
|
34
|
+
| `${CLAUDE_SESSION_ID}` | The current session ID. Useful for logging or creating session-specific files. |
|
|
35
|
+
| `${CLAUDE_SKILL_DIR}` | Absolute path to the skill's directory. Use to reference bundled scripts or files regardless of current working directory. |
|
|
36
|
+
|
|
37
|
+
### Example using `$CLAUDE_SKILL_DIR`
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
---
|
|
41
|
+
name: run-check
|
|
42
|
+
allowed-tools: Bash
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
Run the validation script:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bash ${CLAUDE_SKILL_DIR}/scripts/validate.sh
|
|
49
|
+
```
|
|
50
|
+
```
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: about-liquid-templates
|
|
3
|
+
description: >
|
|
4
|
+
YOU MUST load this skill when working with .tpl. files, deciding whether a file
|
|
5
|
+
needs .tpl. naming, writing LiquidJS syntax in templates, or using custom tags or
|
|
6
|
+
filters. Covers the .tpl. convention, LiquidJS syntax, custom tags, custom filters,
|
|
7
|
+
and a live dump of all variables available in this project.
|
|
8
|
+
user-invocable: false
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# About Liquid Templates
|
|
12
|
+
|
|
13
|
+
Files in this project with `.tpl.` in their filename are processed through LiquidJS
|
|
14
|
+
at compile time. The `.tpl.` segment is stripped from the output filename.
|
|
15
|
+
|
|
16
|
+
## The `.tpl.` Convention
|
|
17
|
+
|
|
18
|
+
| Source file | Processed? | Output file |
|
|
19
|
+
|----------------------|------------|-------------------|
|
|
20
|
+
| `SKILL.md` | No | `SKILL.md` |
|
|
21
|
+
| `SKILL.tpl.md` | Yes | `SKILL.md` |
|
|
22
|
+
| `config.tpl.sh` | Yes | `config.sh` |
|
|
23
|
+
| `README.md` | No | `README.md` |
|
|
24
|
+
|
|
25
|
+
**`SKILL.md` for any skill compiled by sous must always be `SKILL.tpl.md`** — the
|
|
26
|
+
required `## Source for this Skill` footer cannot be rendered without LiquidJS
|
|
27
|
+
processing. See `about-agent-skills` for the full rule.
|
|
28
|
+
|
|
29
|
+
For all other files, use `.tpl.` only when the file genuinely needs variable
|
|
30
|
+
substitution, partials, or conditionals. Static files are copied verbatim — faster
|
|
31
|
+
and safer.
|
|
32
|
+
|
|
33
|
+
## Two Syntaxes — Do Not Mix
|
|
34
|
+
|
|
35
|
+
Sous uses two different variable syntaxes at two different stages. Using the wrong one in
|
|
36
|
+
the wrong place silently produces the literal text instead of a value.
|
|
37
|
+
|
|
38
|
+
| Syntax | Where it belongs | Resolved by |
|
|
39
|
+
|--------|------------------|-------------|
|
|
40
|
+
| {% raw %}`${varName}`{% endraw %} | `_vars` blocks and target paths in a sous **config** file | sous, during config load |
|
|
41
|
+
| {% raw %}`{{ varName }}`{% endraw %} | the body of a `.tpl.` **template** file | LiquidJS, at render time |
|
|
42
|
+
|
|
43
|
+
The one crossover is `@`-include paths, which accept {% raw %}`${var}`{% endraw %}
|
|
44
|
+
substitution because the include processor runs before LiquidJS (see below).
|
|
45
|
+
|
|
46
|
+
## Syntax
|
|
47
|
+
|
|
48
|
+
Output a variable:
|
|
49
|
+
|
|
50
|
+
{% raw %}
|
|
51
|
+
```
|
|
52
|
+
{{ varName }}
|
|
53
|
+
```
|
|
54
|
+
{% endraw %}
|
|
55
|
+
|
|
56
|
+
Conditionals:
|
|
57
|
+
|
|
58
|
+
{% raw %}
|
|
59
|
+
```
|
|
60
|
+
{% if tool == "claude" %}
|
|
61
|
+
...
|
|
62
|
+
{% elsif tool == "codex" %}
|
|
63
|
+
...
|
|
64
|
+
{% else %}
|
|
65
|
+
...
|
|
66
|
+
{% endif %}
|
|
67
|
+
```
|
|
68
|
+
{% endraw %}
|
|
69
|
+
|
|
70
|
+
Loops:
|
|
71
|
+
|
|
72
|
+
{% raw %}
|
|
73
|
+
```
|
|
74
|
+
{% for item in items %}{{ item }}{% endfor %}
|
|
75
|
+
```
|
|
76
|
+
{% endraw %}
|
|
77
|
+
|
|
78
|
+
Assign a variable:
|
|
79
|
+
|
|
80
|
+
{% raw %}
|
|
81
|
+
```
|
|
82
|
+
{% assign name = "value" %}
|
|
83
|
+
```
|
|
84
|
+
{% endraw %}
|
|
85
|
+
|
|
86
|
+
Include another file at render time (path **relative to the template file's directory**):
|
|
87
|
+
|
|
88
|
+
{% raw %}
|
|
89
|
+
```
|
|
90
|
+
{% render "path/to/partial.md" %}
|
|
91
|
+
```
|
|
92
|
+
{% endraw %}
|
|
93
|
+
|
|
94
|
+
`render` resolves paths relative to the template file. For files outside that tree, use
|
|
95
|
+
a path **alias** (`@~sous-shared/...`, `@~project/...`, or a user-defined alias) or a
|
|
96
|
+
`@`-prefixed `${var}` path — the same alias resolution as `@include` (see below) works in
|
|
97
|
+
`render` too.
|
|
98
|
+
|
|
99
|
+
To prevent template sequences from being processed in a code example, wrap the block in
|
|
100
|
+
`raw` / `endraw` tag blocks. These blocks cannot be nested: the first `endraw`
|
|
101
|
+
encountered closes the block, so a nested pair leaks its remainder into the output. Use
|
|
102
|
+
one pair per code example rather than one large wrapper.
|
|
103
|
+
|
|
104
|
+
## `@include` for Cross-Directory Files
|
|
105
|
+
|
|
106
|
+
The `@path` syntax is processed by the build system before LiquidJS runs. It includes
|
|
107
|
+
a file's content inline. Write `@` immediately followed by a `.md` path on its own line
|
|
108
|
+
with nothing else on that line.
|
|
109
|
+
|
|
110
|
+
`@include` works in both `.tpl.` and plain `.md` files. Included content is subject to
|
|
111
|
+
LiquidJS rendering if the parent file is a `.tpl.` (so a Liquid tag inside the included
|
|
112
|
+
file runs in the parent's render pass).
|
|
113
|
+
|
|
114
|
+
The engine sets both `strictVariables: false` and `strictFilters: false`. An undefined
|
|
115
|
+
variable renders as an empty string, and an **unknown filter silently no-ops**, passing
|
|
116
|
+
its input through unchanged. Neither mistake raises an error, so a typo in a variable or
|
|
117
|
+
filter name shows up only as missing or unfiltered output.
|
|
118
|
+
|
|
119
|
+
### Gotcha: `@include` fires inside fenced code blocks
|
|
120
|
+
|
|
121
|
+
The `@include` processor runs on the raw file content *before* LiquidJS and has no
|
|
122
|
+
markdown awareness whatsoever — it matches any line that is nothing but an `@`-prefixed
|
|
123
|
+
`.md` path. A fenced code block does not protect it: an `@path.md` line inside triple
|
|
124
|
+
backticks is still executed and replaced with the file's content. There is no escape
|
|
125
|
+
syntax. To show an `@`-path as an example, put something else on the line (indent it,
|
|
126
|
+
prefix it with a word, or wrap it in backticks inline).
|
|
127
|
+
|
|
128
|
+
### Path forms
|
|
129
|
+
|
|
130
|
+
A `@`-path may be any of:
|
|
131
|
+
|
|
132
|
+
- **Relative** to the including file: `@sections/intro.md` (traverse up with `../`).
|
|
133
|
+
- **Variable-substituted**: `@${sousRootPath}/shared-prompts/x.md` — `${var}` is
|
|
134
|
+
substituted before resolving; if the result is absolute it is used directly.
|
|
135
|
+
- **Aliased**: `@<alias>/rest.md`, where the first segment names a registered alias.
|
|
136
|
+
|
|
137
|
+
### Path aliases
|
|
138
|
+
|
|
139
|
+
The first path segment, up to the first `/` or `:` (both separators work — `@a/b.md`
|
|
140
|
+
≡ `@a:b.md`), is matched against the alias registry. Built-in aliases are reserved and
|
|
141
|
+
always begin with `~`:
|
|
142
|
+
|
|
143
|
+
- `@~sous-shared/...` → the Sous CLI's `shared-prompts` directory (skills, memories,
|
|
144
|
+
`_partials`, etc.). Example: `@~sous-shared/_partials/resume-task.md`.
|
|
145
|
+
- `@~project/...` → the consuming project's root.
|
|
146
|
+
|
|
147
|
+
Projects register their own aliases in settings via an `_aliases` block (root and/or
|
|
148
|
+
project level); names may **not** start with `~` (reserved). An alias value is a string
|
|
149
|
+
or an array of strings (each may use `${var}`):
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
_aliases: { myDocs: "${projectRoot}/docs", shared: ["${a}", "${b}"] }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Resolution order
|
|
156
|
+
|
|
157
|
+
For each `@`-path, candidates are tried in order and the **first that exists on disk
|
|
158
|
+
wins**; if none exist, the build errors listing every path tried:
|
|
159
|
+
|
|
160
|
+
1. Each base of the matched alias, in order (project `_aliases` are tried before root,
|
|
161
|
+
before built-in bases of the same name).
|
|
162
|
+
2. The path resolved **relative to the including file** — using the *full* path
|
|
163
|
+
including the alias segment. So an alias miss can fall through to a real relative
|
|
164
|
+
directory of the same name, letting an alias **augment** a local directory.
|
|
165
|
+
|
|
166
|
+
## Custom Tags
|
|
167
|
+
|
|
168
|
+
**`showVars`** — dumps all variables currently in scope as a fenced JSON block.
|
|
169
|
+
Useful during development to see exactly what variables are available at a given point
|
|
170
|
+
in a template. Remove before finalizing.
|
|
171
|
+
|
|
172
|
+
**`getFiles`** — globs files under a root directory and assigns the resulting array to a
|
|
173
|
+
template variable. It renders nothing; present the results yourself with a `for` loop.
|
|
174
|
+
Each entry has `path`, `dir`, `relPath`, and `name`. `include`/`exclude` take
|
|
175
|
+
comma-separated glob patterns matched relative to `root`, and attribute values may be
|
|
176
|
+
quoted strings or scope variables. The optional `import="<exportName>"` dynamically
|
|
177
|
+
imports each file and attaches that export to the entry (files that fail to import, or
|
|
178
|
+
that lack the export, are dropped) — this is how a manifest of scripts reads its own
|
|
179
|
+
metadata:
|
|
180
|
+
|
|
181
|
+
{% raw %}
|
|
182
|
+
```
|
|
183
|
+
{% getFiles tasks root=scriptsDir include="*.mjs" import="meta" %}
|
|
184
|
+
{% for t in tasks %}
|
|
185
|
+
### {{ t.meta.name }}
|
|
186
|
+
- Script: `{{ t.path }}`
|
|
187
|
+
{% endfor %}
|
|
188
|
+
```
|
|
189
|
+
{% endraw %}
|
|
190
|
+
|
|
191
|
+
**`listFiles`** — the convenience counterpart to `getFiles`: it globs and renders a
|
|
192
|
+
markdown bullet list of file names inline, with no loop needed. Add `relative="true"` to
|
|
193
|
+
render paths relative to the root instead of bare file names:
|
|
194
|
+
|
|
195
|
+
{% raw %}
|
|
196
|
+
```
|
|
197
|
+
{% listFiles root=scriptsDir include="*.mjs" %}
|
|
198
|
+
```
|
|
199
|
+
{% endraw %}
|
|
200
|
+
|
|
201
|
+
**`exportScalarVarsJs`** — emits every in-scope scalar variable (string, finite number,
|
|
202
|
+
boolean) as an ES module default export, keys sorted. Objects, arrays, `null` and
|
|
203
|
+
non-finite numbers are skipped. Use it to compile a settings module that runtime code
|
|
204
|
+
imports, rather than re-deriving project configuration:
|
|
205
|
+
|
|
206
|
+
{% raw %}
|
|
207
|
+
```
|
|
208
|
+
{% exportScalarVarsJs %}
|
|
209
|
+
```
|
|
210
|
+
{% endraw %}
|
|
211
|
+
|
|
212
|
+
## Custom Filters
|
|
213
|
+
|
|
214
|
+
**`bulletList`** — converts an array variable to a markdown bullet list:
|
|
215
|
+
|
|
216
|
+
{% raw %}
|
|
217
|
+
```
|
|
218
|
+
{{ tags | bulletList }}
|
|
219
|
+
```
|
|
220
|
+
{% endraw %}
|
|
221
|
+
|
|
222
|
+
Output (if `tags` is `["a", "b", "c"]`):
|
|
223
|
+
```
|
|
224
|
+
- a
|
|
225
|
+
- b
|
|
226
|
+
- c
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Given a non-array value, `bulletList` returns it as a plain string with no bullet.
|
|
230
|
+
|
|
231
|
+
## Authoring Guidelines
|
|
232
|
+
|
|
233
|
+
Templates (`.tpl.*` files) must be **maximally reusable**. A well-written template
|
|
234
|
+
can be copied between projects or shared across teams without edits — only the
|
|
235
|
+
project's variables change.
|
|
236
|
+
|
|
237
|
+
**Rules:**
|
|
238
|
+
|
|
239
|
+
1. **Never hard-code values that could differ between projects.** If a value comes
|
|
240
|
+
from project configuration (board IDs, project keys, sprint IDs, URLs, user info),
|
|
241
|
+
use a variable. When no suitable variable exists, add one to the project's sous
|
|
242
|
+
config file (the file that defines the project's `_vars`).
|
|
243
|
+
2. **Guard project-specific blocks with conditionals.** If a section only applies when
|
|
244
|
+
a variable is set, wrap it in {% raw %}`{% if varName %} ... {% endif %}`{% endraw %} so the
|
|
245
|
+
block disappears cleanly for projects that don't define it.
|
|
246
|
+
3. **Prefer derived variables over raw values.** Example: `ticketPrefix` is derived
|
|
247
|
+
from `jiraProjectKey` — templates use `ticketPrefix` so they stay correct if the
|
|
248
|
+
key changes.
|
|
249
|
+
4. **Test portability mentally.** Before finalizing a template, ask: "If I compiled
|
|
250
|
+
this for a different project with different settings, would the output still make
|
|
251
|
+
sense?" If not, parameterize the varying part.
|
|
252
|
+
|
|
253
|
+
## Reference Files
|
|
254
|
+
|
|
255
|
+
- [liquid-filters.md](references/liquid-filters.md) — complete standard LiquidJS filter catalogue (string, array, number, date, default)
|
|
256
|
+
|
|
257
|
+
## Available Variables
|
|
258
|
+
|
|
259
|
+
The following variables are in scope at compile time in this project:
|
|
260
|
+
|
|
261
|
+
{% showVars %}
|
|
262
|
+
|
|
263
|
+
## Source for this Skill
|
|
264
|
+
|
|
265
|
+
This skill was pulled from the `sous` project's "shared skills" library. It was compiled from a template and
|
|
266
|
+
the output file should not be edited directly.
|
|
267
|
+
|
|
268
|
+
- Source Path: {{ sousTemplatePath }}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# LiquidJS Built-in Filters
|
|
2
|
+
|
|
3
|
+
Standard filters available in all `.tpl.` files. Chained with `|`.
|
|
4
|
+
|
|
5
|
+
## String
|
|
6
|
+
|
|
7
|
+
| Filter | Description | Example |
|
|
8
|
+
|:-------|:------------|:--------|
|
|
9
|
+
| `upcase` | Uppercase | `{{ "hello" | upcase }}` → `HELLO` |
|
|
10
|
+
| `downcase` | Lowercase | `{{ "HELLO" | downcase }}` → `hello` |
|
|
11
|
+
| `capitalize` | First char uppercase | `{{ "hello world" | capitalize }}` → `Hello world` |
|
|
12
|
+
| `strip` | Remove leading/trailing whitespace | `{{ " hi " | strip }}` → `hi` |
|
|
13
|
+
| `lstrip` | Remove leading whitespace | |
|
|
14
|
+
| `rstrip` | Remove trailing whitespace | |
|
|
15
|
+
| `strip_newlines` | Remove all newlines | |
|
|
16
|
+
| `strip_html` | Remove HTML tags | |
|
|
17
|
+
| `escape` | HTML-escape `<`, `>`, `&`, `"` | |
|
|
18
|
+
| `url_encode` | Percent-encode a URL string | |
|
|
19
|
+
| `url_decode` | Decode a percent-encoded string | |
|
|
20
|
+
| `append` | Append a string | `{{ "foo" | append: "bar" }}` → `foobar` |
|
|
21
|
+
| `prepend` | Prepend a string | `{{ "bar" | prepend: "foo" }}` → `foobar` |
|
|
22
|
+
| `replace` | Replace all occurrences | `{{ "aabbcc" | replace: "b", "x" }}` → `aaxxcc` |
|
|
23
|
+
| `replace_first` | Replace first occurrence | |
|
|
24
|
+
| `remove` | Remove all occurrences | `{{ "aabbcc" | remove: "b" }}` → `aacc` |
|
|
25
|
+
| `remove_first` | Remove first occurrence | |
|
|
26
|
+
| `truncate` | Truncate to N chars (adds `...`) | `{{ "hello world" | truncate: 7 }}` → `hell...` |
|
|
27
|
+
| `truncatewords` | Truncate to N words | `{{ "one two three" | truncatewords: 2 }}` → `one two...` |
|
|
28
|
+
| `split` | Split string into array | `{{ "a,b,c" | split: "," }}` → `["a","b","c"]` |
|
|
29
|
+
| `newline_to_br` | Replace `\n` with `<br>` | |
|
|
30
|
+
|
|
31
|
+
## Array
|
|
32
|
+
|
|
33
|
+
| Filter | Description | Example |
|
|
34
|
+
|:-------|:------------|:--------|
|
|
35
|
+
| `join` | Join array with separator | `{{ arr | join: ", " }}` |
|
|
36
|
+
| `first` | First element | `{{ arr | first }}` |
|
|
37
|
+
| `last` | Last element | `{{ arr | last }}` |
|
|
38
|
+
| `reverse` | Reverse order | |
|
|
39
|
+
| `sort` | Sort ascending (case-sensitive) | |
|
|
40
|
+
| `sort_natural` | Sort ascending (case-insensitive) | |
|
|
41
|
+
| `uniq` | Remove duplicates | |
|
|
42
|
+
| `compact` | Remove nil/falsy values | |
|
|
43
|
+
| `map` | Extract a property from each object | `{{ items | map: "name" }}` |
|
|
44
|
+
| `where` | Filter objects by property value | `{{ items | where: "active", true }}` |
|
|
45
|
+
| `concat` | Concatenate two arrays | `{{ arr1 | concat: arr2 }}` |
|
|
46
|
+
| `slice` | Extract a sub-array | `{{ arr | slice: 1, 3 }}` |
|
|
47
|
+
| `size` | Length of string or array | `{{ arr | size }}` |
|
|
48
|
+
| `push` | Append element to array | |
|
|
49
|
+
| `pop` | Remove last element | |
|
|
50
|
+
| `shift` | Remove first element | |
|
|
51
|
+
| `unshift` | Prepend element to array | |
|
|
52
|
+
|
|
53
|
+
## Number
|
|
54
|
+
|
|
55
|
+
| Filter | Description | Example |
|
|
56
|
+
|:-------|:------------|:--------|
|
|
57
|
+
| `plus` | Add | `{{ 4 | plus: 2 }}` → `6` |
|
|
58
|
+
| `minus` | Subtract | `{{ 4 | minus: 2 }}` → `2` |
|
|
59
|
+
| `times` | Multiply | `{{ 4 | times: 2 }}` → `8` |
|
|
60
|
+
| `divided_by` | Divide | `{{ 10 | divided_by: 2 }}` → `5` |
|
|
61
|
+
| `modulo` | Modulus | `{{ 10 | modulo: 3 }}` → `1` |
|
|
62
|
+
| `abs` | Absolute value | `{{ -4 | abs }}` → `4` |
|
|
63
|
+
| `ceil` | Round up | `{{ 4.1 | ceil }}` → `5` |
|
|
64
|
+
| `floor` | Round down | `{{ 4.9 | floor }}` → `4` |
|
|
65
|
+
| `round` | Round to nearest (or N decimal places) | `{{ 4.567 | round: 2 }}` → `4.57` |
|
|
66
|
+
| `at_least` | Clamp to minimum | `{{ 3 | at_least: 5 }}` → `5` |
|
|
67
|
+
| `at_most` | Clamp to maximum | `{{ 7 | at_most: 5 }}` → `5` |
|
|
68
|
+
|
|
69
|
+
## Other
|
|
70
|
+
|
|
71
|
+
| Filter | Description | Example |
|
|
72
|
+
|:-------|:------------|:--------|
|
|
73
|
+
| `default` | Fallback if nil/empty/false | `{{ val | default: "n/a" }}` |
|
|
74
|
+
| `date` | Format a date | `{{ "now" | date: "%Y-%m-%d" }}` |
|
|
75
|
+
| `size` | Length of string or array | `{{ "hello" | size }}` → `5` |
|
|
76
|
+
| `json` | Serialize to JSON string | `{{ obj | json }}` |
|
|
77
|
+
|
|
78
|
+
## Sous Custom Filters
|
|
79
|
+
|
|
80
|
+
| Filter | Description |
|
|
81
|
+
|:-------|:------------|
|
|
82
|
+
| `bulletList` | Convert array to markdown bullet list (`- item` per line) |
|