Skip to content

Commit 5bd959e

Browse files
committed
feat(config): add SKILLC_HOME environment variable override
Per [[RFC-0009:C-ENV-OVERRIDE]], adds SKILLC_HOME env var to override home directory detection for all global paths. This enables: - Cross-platform test isolation (HOME only works on Unix) - Custom installation locations - Portable/relocatable installations Also fixes MCP skc_lint and skc_build to use resolve_skill() instead of global_source_store(), enabling project-local skill resolution. Test changes: - All integration tests now automatically isolated via SKILLC_HOME - Removed with_mock_home() - mock home is always created - Unified run_skc() always sets SKILLC_HOME - Removed cfg(unix) from 16 tests - now cross-platform - MCP tests use spawn_with_context() for consistent isolation
1 parent 7a2638c commit 5bd959e

22 files changed

+1150
-1497
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.1] - 2026-01-31
11+
12+
### Added
13+
14+
- `SKILLC_HOME` env var overrides home directory detection (WI-2026-01-31-001)
15+
16+
### Fixed
17+
18+
- MCP lint/build use resolve_skill() (WI-2026-01-31-002)
19+
1020
## [0.1.0] - 2026-01-30
1121

1222
### Added

Cargo.lock

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "skillc"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
edition = "2024"
55
license = "MIT OR Apache-2.0"
66
description = "A development kit for Agent Skills - the open format for extending AI agent capabilities"
@@ -70,4 +70,5 @@ assert_cmd = "2"
7070
filetime = "0.2"
7171
insta = { version = "1", features = ["json"] }
7272
predicates = "3"
73+
temp-env = "0.3"
7374
tempfile = "3"

docs/rfc/RFC-0009.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<!-- GENERATED: do not edit. Source: RFC-0009 -->
2-
<!-- SIGNATURE: sha256:afbd12b3a17ce6d4c52fd6434461004ac091b259e824151cc4698a43fdb828d0 -->
2+
<!-- SIGNATURE: sha256:e1e5f61c6203e7687beabd4d56b4d497a29cd181816aacd597a1411b458f2f6f -->
33

44
# RFC-0009: Configuration
55

6-
> **Version:** 0.1.0 | **Status:** normative | **Phase:** test
6+
> **Version:** 0.2.0 | **Status:** normative | **Phase:** test
77
88
---
99

@@ -137,10 +137,33 @@ Changing the tokenizer setting invalidates existing search indexes. The search c
137137

138138
*Since: v0.1.0*
139139

140+
### [RFC-0009:C-ENV-OVERRIDE] Environment Variable Override (Normative) <a id="rfc-0009c-env-override"></a>
141+
142+
The `SKILLC_HOME` environment variable, if set, MUST override the default home directory detection for all global paths.
143+
144+
When `SKILLC_HOME` is set to a path (e.g., `/custom/home`):
145+
- `global_skillc_dir()` MUST return `$SKILLC_HOME/.skillc/` (e.g., `/custom/home/.skillc/`)
146+
- All derived paths (global source store, global runtime, etc.) MUST use this base
147+
- `find_project_root()` MUST exclude `$SKILLC_HOME` from project root detection
148+
149+
When `SKILLC_HOME` is not set:
150+
- The implementation MUST fall back to `dirs::home_dir()` behavior
151+
152+
**Rationale:** This enables:
153+
1. Cross-platform test isolation (HOME env var only works on Unix)
154+
2. Custom installation locations
155+
3. Portable/relocatable installations
156+
157+
*Since: v0.2.0*
158+
140159
---
141160

142161
## Changelog
143162

163+
### v0.2.0 (2026-01-31)
164+
165+
Add SKILLC_HOME override
166+
144167
### v0.1.0 (2026-01-30)
145168

146169
Initial release

gov/releases.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
[[releases]]
2+
version = "0.1.1"
3+
date = "2026-01-31"
4+
refs = [
5+
"WI-2026-01-31-001",
6+
"WI-2026-01-31-002",
7+
]
8+
19
[[releases]]
210
version = "0.1.0"
311
date = "2026-01-30"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"clause_id": "C-ENV-OVERRIDE",
3+
"title": "Environment Variable Override",
4+
"kind": "normative",
5+
"status": "active",
6+
"text": "The `SKILLC_HOME` environment variable, if set, MUST override the default home directory detection for all global paths.\n\nWhen `SKILLC_HOME` is set to a path (e.g., `/custom/home`):\n- `global_skillc_dir()` MUST return `$SKILLC_HOME/.skillc/` (e.g., `/custom/home/.skillc/`)\n- All derived paths (global source store, global runtime, etc.) MUST use this base\n- `find_project_root()` MUST exclude `$SKILLC_HOME` from project root detection\n\nWhen `SKILLC_HOME` is not set:\n- The implementation MUST fall back to `dirs::home_dir()` behavior\n\n**Rationale:** This enables:\n1. Cross-platform test isolation (HOME env var only works on Unix)\n2. Custom installation locations\n3. Portable/relocatable installations",
7+
"since": "0.2.0"
8+
}

gov/rfc/RFC-0009/rfc.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"rfc_id": "RFC-0009",
33
"title": "Configuration",
4-
"version": "0.1.0",
4+
"version": "0.2.0",
55
"status": "normative",
66
"phase": "test",
77
"owners": ["@govctl-org"],
88
"created": "2026-01-30",
9-
"updated": "2026-01-30",
9+
"updated": "2026-01-31",
1010
"sections": [
1111
{
1212
"title": "Summary",
@@ -17,15 +17,22 @@
1717
"clauses": [
1818
"clauses/C-FILES.json",
1919
"clauses/C-RESOLUTION.json",
20-
"clauses/C-TOKENIZER.json"
20+
"clauses/C-TOKENIZER.json",
21+
"clauses/C-ENV-OVERRIDE.json"
2122
]
2223
}
2324
],
2425
"changelog": [
26+
{
27+
"version": "0.2.0",
28+
"date": "2026-01-31",
29+
"notes": "Add SKILLC_HOME override"
30+
},
2531
{
2632
"version": "0.1.0",
2733
"date": "2026-01-30",
2834
"notes": "Initial release"
2935
}
30-
]
36+
],
37+
"signature": "e1e5f61c6203e7687beabd4d56b4d497a29cd181816aacd597a1411b458f2f6f"
3138
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[govctl]
2+
schema = 1
3+
id = "WI-2026-01-31-001"
4+
title = "Add SKILLC_HOME environment variable override"
5+
status = "done"
6+
created = "2026-01-31"
7+
started = "2026-01-31"
8+
completed = "2026-01-31"
9+
refs = ["RFC-0009"]
10+
11+
[content]
12+
description = "Add `SKILLC_HOME` environment variable to override home directory detection, enabling cross-platform test isolation and custom installation locations."
13+
14+
[[content.acceptance_criteria]]
15+
text = "`SKILLC_HOME` env var overrides home directory detection"
16+
status = "done"
17+
category = "added"
18+
19+
[[content.acceptance_criteria]]
20+
text = "Cross-platform test isolation using `SKILLC_HOME`"
21+
status = "done"
22+
category = "chore"
23+
24+
[[content.acceptance_criteria]]
25+
text = "All `cfg(unix)` tests become cross-platform"
26+
status = "done"
27+
category = "chore"
28+
29+
[[content.acceptance_criteria]]
30+
text = "All tests pass"
31+
status = "done"
32+
category = "chore"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[govctl]
2+
schema = 1
3+
id = "WI-2026-01-31-002"
4+
title = "fix(mcp): use resolve_skill for lint and build tools"
5+
status = "done"
6+
created = "2026-01-31"
7+
started = "2026-01-31"
8+
completed = "2026-01-31"
9+
10+
[content]
11+
description = "Fix MCP skc_lint and skc_build to use resolve_skill() instead of global_source_store(), enabling project-local skill resolution. Refactor MCP tests to use spawn_with_context() for consistent isolation."
12+
13+
[[content.acceptance_criteria]]
14+
text = "MCP lint/build use resolve_skill()"
15+
status = "done"
16+
category = "fixed"
17+
18+
[[content.acceptance_criteria]]
19+
text = "MCP tests use spawn_with_context()"
20+
status = "done"
21+
category = "chore"
22+
23+
[[content.acceptance_criteria]]
24+
text = "All tests pass"
25+
status = "done"
26+
category = "chore"

src/config.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,19 @@ pub fn get_tokenizer() -> Tokenizer {
280280
Tokenizer::default()
281281
}
282282

283-
/// Get the global skillc directory (~/.skillc/)
283+
/// Get the global skillc directory.
284+
///
285+
/// Per [[RFC-0009:C-ENV-OVERRIDE]], checks `SKILLC_HOME` first, then falls back to `~/.skillc/`.
286+
/// When `SKILLC_HOME` is set, it acts as the home directory override, so `.skillc` is appended.
284287
///
285288
/// Returns error if home directory cannot be determined.
286289
pub fn global_skillc_dir() -> Result<PathBuf> {
290+
// Check SKILLC_HOME override first (enables cross-platform test isolation)
291+
// SKILLC_HOME acts as home directory override, so we append .skillc
292+
if let Ok(skillc_home) = env::var("SKILLC_HOME") {
293+
return Ok(PathBuf::from(skillc_home).join(".skillc"));
294+
}
295+
287296
let home = dirs::home_dir()
288297
.ok_or_else(|| SkillcError::Internal("could not determine home directory".to_string()))?;
289298
Ok(home.join(".skillc"))
@@ -297,14 +306,21 @@ pub fn global_source_store() -> Result<PathBuf> {
297306
/// Find project root by walking up from CWD, looking for `.skillc/` directory.
298307
///
299308
/// Returns the project root directory (containing `.skillc/`) if found.
300-
/// Note: The home directory is excluded - `~/.skillc/` is the global store, not a project.
309+
/// Note: The home directory (or SKILLC_HOME) is excluded - its `.skillc/` is the global store, not a project.
301310
pub fn find_project_root() -> Option<PathBuf> {
302-
let home = dirs::home_dir();
311+
// Get the home directory to exclude from project root search
312+
// If SKILLC_HOME is set, use that; otherwise use the real home
313+
let excluded_home = if let Ok(skillc_home) = env::var("SKILLC_HOME") {
314+
Some(PathBuf::from(skillc_home))
315+
} else {
316+
dirs::home_dir()
317+
};
318+
303319
let mut dir = env::current_dir().ok()?;
304320

305321
loop {
306-
// Don't treat home directory as project root (that's the global store)
307-
if Some(&dir) != home.as_ref() && dir.join(".skillc").is_dir() {
322+
// Don't treat home directory as project root (its .skillc is the global store)
323+
if Some(&dir) != excluded_home.as_ref() && dir.join(".skillc").is_dir() {
308324
return Some(dir);
309325
}
310326
if !dir.pop() {

0 commit comments

Comments
 (0)