teamcast 1.0.3 → 1.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/README.md +46 -2
- package/dist/application/validate-team.js +3 -0
- package/dist/cli/manage.js +10 -2
- package/dist/core/environment-resolver.js +20 -21
- package/dist/generator/index.js +2 -0
- package/dist/registry/environment-schema.js +65 -0
- package/dist/registry/environments.js +11 -99
- package/dist/registry/resource-loader.js +78 -0
- package/dist/registry/types.js +71 -4
- package/dist/renderers/codex/skill-md.js +1 -1
- package/dist/validator/checks/manifest-registry.js +1 -1
- package/dist/validator/checks/team-graph-enhanced.js +19 -0
- package/dist/validator/index.js +2 -2
- package/dist/wizard/index.js +2 -1
- package/dist/wizard/steps/environment-selection.js +1 -1
- package/package.json +1 -1
- package/templates/environments/docker.yaml +36 -0
- package/templates/environments/go.yaml +40 -0
- package/templates/environments/java.yaml +41 -0
- package/templates/environments/node.yaml +39 -0
- package/templates/environments/python.yaml +41 -0
- package/templates/environments/ruby.yaml +39 -0
- package/templates/environments/rust.yaml +41 -0
- package/templates/environments/terraform.yaml +31 -0
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ TeamCast now uses a canonical manifest shape with target-specific blocks:
|
|
|
69
69
|
- `claude.agents.<name>` - native Claude Code runtime fields and doc outputs
|
|
70
70
|
- `codex.agents.<name>` - native Codex runtime fields and TOML outputs
|
|
71
71
|
- `<target>.agents.<name>.forge` - TeamCast-only metadata such as delegation graph
|
|
72
|
-
- `project.environments` - active project environments
|
|
72
|
+
- `project.environments` - active project environments (`node`, `python`, `go`, `rust`, `java`, `ruby`, `docker`, `terraform`) — auto-detected or explicit
|
|
73
73
|
|
|
74
74
|
TeamCast includes a built-in registry of capabilities, traits, instruction fragments, policy fragments, models, and skills. These are not serialized into `teamcast.yaml`.
|
|
75
75
|
|
|
@@ -526,6 +526,50 @@ This means a **reviewer** (read + execute, no write) gets code patterns but NOT
|
|
|
526
526
|
|
|
527
527
|
Custom agents work the same way — a `react-dev` with `write_files` + `execute` automatically gets the right fragments without any role-name matching.
|
|
528
528
|
|
|
529
|
+
#### Built-in environments
|
|
530
|
+
|
|
531
|
+
| Environment | Auto-detected by | Policy allows |
|
|
532
|
+
|---|---|---|
|
|
533
|
+
| `node` | `package.json` | `npm`, `npx`, `node` |
|
|
534
|
+
| `python` | `pyproject.toml`, `requirements.txt`, `setup.py` | `pytest`, `python`, `uv`, `poetry` |
|
|
535
|
+
| `go` | `go.mod` | `go build/test/run/vet/mod` |
|
|
536
|
+
| `rust` | `Cargo.toml` | `cargo`, `rustfmt`, `clippy` |
|
|
537
|
+
| `java` | `pom.xml`, `build.gradle` | `mvn`, `gradle`, `./gradlew` |
|
|
538
|
+
| `ruby` | `Gemfile` | `bundle`, `rake`, `rspec` |
|
|
539
|
+
| `docker` | `Dockerfile`, `docker-compose.yml` | `docker`, `docker compose` |
|
|
540
|
+
| `terraform` | `main.tf`, `terraform.tf` | `terraform init/plan/validate/fmt` |
|
|
541
|
+
|
|
542
|
+
#### Custom environments
|
|
543
|
+
|
|
544
|
+
Drop a YAML file into `.agentforge/environments/` to add a new environment or override a builtin:
|
|
545
|
+
|
|
546
|
+
```yaml
|
|
547
|
+
# .agentforge/environments/bun.yaml
|
|
548
|
+
id: bun
|
|
549
|
+
description: "Bun runtime environment"
|
|
550
|
+
detect_files:
|
|
551
|
+
- bun.lockb
|
|
552
|
+
policy_rules:
|
|
553
|
+
sandbox:
|
|
554
|
+
enabled: true
|
|
555
|
+
allow:
|
|
556
|
+
- "Bash(bun *)"
|
|
557
|
+
instruction_fragments:
|
|
558
|
+
bun_patterns:
|
|
559
|
+
content: |
|
|
560
|
+
This project uses Bun.
|
|
561
|
+
Use `bun install`, `bun run`, and `bun test`.
|
|
562
|
+
requires_capabilities:
|
|
563
|
+
- read_files
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Reference it in `teamcast.yaml` by id:
|
|
567
|
+
|
|
568
|
+
```yaml
|
|
569
|
+
project:
|
|
570
|
+
environments: [node, bun]
|
|
571
|
+
```
|
|
572
|
+
|
|
529
573
|
### Instruction Layers
|
|
530
574
|
|
|
531
575
|
Agent prompts are composed from three layers:
|
|
@@ -534,7 +578,7 @@ Agent prompts are composed from three layers:
|
|
|
534
578
|
|-------|--------|-------|
|
|
535
579
|
| **instruction_blocks** | `teamcast.yaml` or preset | Project-specific behavior, workflow rules |
|
|
536
580
|
| **instruction_fragments** | Built-in registry | Reusable role patterns (e.g. `feature-developer-workflow`) |
|
|
537
|
-
| **environment instructions** | Built-in environments | Toolchain best practices, injected by capability |
|
|
581
|
+
| **environment instructions** | Built-in + custom environments | Toolchain best practices, injected by capability |
|
|
538
582
|
|
|
539
583
|
Presets provide sensible defaults for `instruction_blocks` and `instruction_fragments`. For deeper customization, edit `teamcast.yaml` and run `teamcast generate`.
|
|
540
584
|
|
|
@@ -7,6 +7,7 @@ import { hasErrors, printManifestValidationSummary, } from '../validator/reporte
|
|
|
7
7
|
import { getTarget, getRegisteredTargetNames } from '../renderers/registry.js';
|
|
8
8
|
import { applyEnvironmentInstructions, resolveEnvironmentIds, resolveEnvironmentPolicies, } from '../core/environment-resolver.js';
|
|
9
9
|
import { checkManifestRegistry } from '../validator/checks/manifest-registry.js';
|
|
10
|
+
import { builtinResourceLoader } from '../registry/resource-loader.js';
|
|
10
11
|
export function evaluateTeam(manifest, options) {
|
|
11
12
|
const schemaResult = validateSchema(manifest);
|
|
12
13
|
if (!schemaResult.valid) {
|
|
@@ -15,6 +16,8 @@ export function evaluateTeam(manifest, options) {
|
|
|
15
16
|
validationResults: [],
|
|
16
17
|
};
|
|
17
18
|
}
|
|
19
|
+
if (options?.cwd)
|
|
20
|
+
builtinResourceLoader.loadUserResources(options.cwd);
|
|
18
21
|
const rawManifest = applyDefaults(schemaResult.data);
|
|
19
22
|
const resolvedManifest = options?.cwd ? resolveEnvironmentPolicies(rawManifest, options.cwd) : rawManifest;
|
|
20
23
|
const manifestRegistryResults = checkManifestRegistry(resolvedManifest);
|
package/dist/cli/manage.js
CHANGED
|
@@ -6,7 +6,7 @@ import { writeManifest } from '../manifest/writer.js';
|
|
|
6
6
|
import { expandCapabilities } from '../core/capability-resolver.js';
|
|
7
7
|
import { generate } from '../generator/index.js';
|
|
8
8
|
import { defaultRegistry } from '../registry/index.js';
|
|
9
|
-
import { printSuccess, printError, printHeader, printCommandSuccess, } from '../utils/chalk-helpers.js';
|
|
9
|
+
import { printSuccess, printError, printHeader, printCommandSuccess, printNextSteps, } from '../utils/chalk-helpers.js';
|
|
10
10
|
import { getTarget, getRegisteredTargetNames } from '../renderers/registry.js';
|
|
11
11
|
import { evaluateTeam, teamHasBlockingIssues, printManifestValidation, } from '../application/validate-team.js';
|
|
12
12
|
import { promptConfirm, promptInput, promptList, promptCheckbox, } from '../utils/prompts.js';
|
|
@@ -248,6 +248,10 @@ export async function runAddAgentCommand(name, options) {
|
|
|
248
248
|
const nextTeam = addAgentToTeam(team, name, agent);
|
|
249
249
|
applyManifestChanges(cwd, manifest, targetName, nextTeam);
|
|
250
250
|
printCommandSuccess(`Agent "${name}" added and configuration regenerated`);
|
|
251
|
+
printNextSteps([
|
|
252
|
+
`Open ${chalk.bold('teamcast.yaml')} and fill in agent instructions based on ${chalk.yellow('// TODO')} comments`,
|
|
253
|
+
`Run ${chalk.bold('teamcast generate')} to apply your changes`,
|
|
254
|
+
]);
|
|
251
255
|
}
|
|
252
256
|
export async function runCreateSkillCommand(name, options) {
|
|
253
257
|
const cwd = process.cwd();
|
|
@@ -407,7 +411,11 @@ async function promptAgentConfig(name, targetContext) {
|
|
|
407
411
|
instructions: [
|
|
408
412
|
{
|
|
409
413
|
kind: 'behavior',
|
|
410
|
-
content: `You are ${name}
|
|
414
|
+
content: `You are ${name}.\n// TODO: Describe the agent's core personality, rules, and constraints here.\n// Example: "You are a strict security auditor. Never trust user input."`,
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
kind: 'workflow',
|
|
418
|
+
content: `// TODO: Define the step-by-step process the agent should follow.\n// 1. Read the provided context.\n// 2. Perform analysis.\n// 3. Output the result.`,
|
|
411
419
|
},
|
|
412
420
|
],
|
|
413
421
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TARGET_NAMES, getManifestTargetConfig, setManifestTargetConfig } from '../manifest/targets.js';
|
|
2
2
|
import { getEnvironment, detectEnvironments } from '../registry/environments.js';
|
|
3
|
-
import { isEnvironmentId } from '../registry/
|
|
3
|
+
import { isEnvironmentId } from '../registry/environments.js';
|
|
4
4
|
import { agentHasCapability } from './capability-resolver.js';
|
|
5
5
|
/**
|
|
6
6
|
* Resolves environment IDs from the manifest, combining:
|
|
@@ -80,31 +80,30 @@ function mergePoliciesSimple(base, extra) {
|
|
|
80
80
|
}
|
|
81
81
|
: undefined,
|
|
82
82
|
hooks: base.hooks || extra.hooks
|
|
83
|
-
? {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
: undefined,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
83
|
+
? (() => {
|
|
84
|
+
const pre = [...(base.hooks?.pre_tool_use ?? []), ...(extra.hooks?.pre_tool_use ?? [])];
|
|
85
|
+
const post = [...(base.hooks?.post_tool_use ?? []), ...(extra.hooks?.post_tool_use ?? [])];
|
|
86
|
+
const notif = [...(base.hooks?.notification ?? []), ...(extra.hooks?.notification ?? [])];
|
|
87
|
+
return {
|
|
88
|
+
pre_tool_use: pre.length > 0 ? pre : undefined,
|
|
89
|
+
post_tool_use: post.length > 0 ? post : undefined,
|
|
90
|
+
notification: notif.length > 0 ? notif : undefined,
|
|
91
|
+
};
|
|
92
|
+
})()
|
|
94
93
|
: undefined,
|
|
95
94
|
network: base.network || extra.network
|
|
96
|
-
? {
|
|
97
|
-
|
|
95
|
+
? (() => {
|
|
96
|
+
const domains = [...new Set([
|
|
98
97
|
...(base.network?.allowed_domains ?? []),
|
|
99
98
|
...(extra.network?.allowed_domains ?? []),
|
|
100
|
-
])]
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
: undefined,
|
|
105
|
-
assertions: [...(base.assertions ?? []), ...(extra.assertions ?? [])].length > 0
|
|
106
|
-
? [...(base.assertions ?? []), ...(extra.assertions ?? [])]
|
|
99
|
+
])];
|
|
100
|
+
return { allowed_domains: domains.length > 0 ? domains : undefined };
|
|
101
|
+
})()
|
|
107
102
|
: undefined,
|
|
103
|
+
assertions: (() => {
|
|
104
|
+
const merged = [...(base.assertions ?? []), ...(extra.assertions ?? [])];
|
|
105
|
+
return merged.length > 0 ? merged : undefined;
|
|
106
|
+
})(),
|
|
108
107
|
};
|
|
109
108
|
}
|
|
110
109
|
function resolveTargetPolicies(envPolicies, targetConfig) {
|
package/dist/generator/index.js
CHANGED
|
@@ -3,7 +3,9 @@ import { buildGeneratedOutputs } from '../application/build-generated-files.js';
|
|
|
3
3
|
import { getRegisteredTargetNames, getTarget } from '../renderers/registry.js';
|
|
4
4
|
import { normalizeManifest } from '../manifest/normalize.js';
|
|
5
5
|
import { applyEnvironmentInstructions, resolveEnvironmentIds, resolveEnvironmentPolicies, } from '../core/environment-resolver.js';
|
|
6
|
+
import { builtinResourceLoader } from '../registry/resource-loader.js';
|
|
6
7
|
export function generate(manifest, options) {
|
|
8
|
+
builtinResourceLoader.loadUserResources(options.cwd);
|
|
7
9
|
const rawManifest = resolveEnvironmentPolicies(applyDefaults(manifest), options.cwd);
|
|
8
10
|
const envIds = resolveEnvironmentIds(rawManifest, options.cwd);
|
|
9
11
|
const rawManifestRecord = rawManifest;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Environment YAML schema — parse and convert YAML environment definitions
|
|
2
|
+
// to runtime EnvironmentDef objects.
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { isCapability } from './types.js';
|
|
6
|
+
// --- Validation ---
|
|
7
|
+
export function parseEnvironmentYaml(raw) {
|
|
8
|
+
if (!raw || typeof raw !== 'object') {
|
|
9
|
+
throw new Error('Environment definition must be an object');
|
|
10
|
+
}
|
|
11
|
+
const obj = raw;
|
|
12
|
+
if (typeof obj.id !== 'string' || !obj.id) {
|
|
13
|
+
throw new Error('Environment definition requires a non-empty "id" field');
|
|
14
|
+
}
|
|
15
|
+
if (typeof obj.description !== 'string') {
|
|
16
|
+
throw new Error(`Environment "${obj.id}": "description" must be a string`);
|
|
17
|
+
}
|
|
18
|
+
if (obj.detect_files !== undefined) {
|
|
19
|
+
if (!Array.isArray(obj.detect_files) || !obj.detect_files.every((f) => typeof f === 'string')) {
|
|
20
|
+
throw new Error(`Environment "${obj.id}": "detect_files" must be a string array`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (!obj.policy_rules || typeof obj.policy_rules !== 'object') {
|
|
24
|
+
throw new Error(`Environment "${obj.id}": "policy_rules" must be an object`);
|
|
25
|
+
}
|
|
26
|
+
const policies = obj.policy_rules;
|
|
27
|
+
if (policies.allow !== undefined) {
|
|
28
|
+
if (!Array.isArray(policies.allow) || !policies.allow.every((a) => typeof a === 'string')) {
|
|
29
|
+
throw new Error(`Environment "${obj.id}": "policy_rules.allow" must be a string array`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!obj.instruction_fragments || typeof obj.instruction_fragments !== 'object') {
|
|
33
|
+
throw new Error(`Environment "${obj.id}": "instruction_fragments" must be an object`);
|
|
34
|
+
}
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
// --- Conversion to runtime EnvironmentDef ---
|
|
38
|
+
function toEnvironmentInstruction(value) {
|
|
39
|
+
if (typeof value === 'string')
|
|
40
|
+
return value;
|
|
41
|
+
return {
|
|
42
|
+
content: value.content,
|
|
43
|
+
requires_capabilities: value.requires_capabilities.filter(isCapability),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function environmentYamlToDef(yaml) {
|
|
47
|
+
const fragments = {};
|
|
48
|
+
for (const [key, value] of Object.entries(yaml.instruction_fragments)) {
|
|
49
|
+
fragments[key] = toEnvironmentInstruction(value);
|
|
50
|
+
}
|
|
51
|
+
const def = {
|
|
52
|
+
id: yaml.id,
|
|
53
|
+
description: yaml.description,
|
|
54
|
+
policyRules: {
|
|
55
|
+
sandbox: yaml.policy_rules.sandbox,
|
|
56
|
+
allow: yaml.policy_rules.allow,
|
|
57
|
+
},
|
|
58
|
+
instructionFragments: fragments,
|
|
59
|
+
};
|
|
60
|
+
if (yaml.detect_files?.length) {
|
|
61
|
+
const files = yaml.detect_files;
|
|
62
|
+
def.detect = (cwd) => files.some((file) => existsSync(join(cwd, file)));
|
|
63
|
+
}
|
|
64
|
+
return def;
|
|
65
|
+
}
|
|
@@ -1,105 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
description: 'Node.js environment, auto-detected via package.json',
|
|
7
|
-
detect: (cwd) => existsSync(join(cwd, 'package.json')),
|
|
8
|
-
policyRules: {
|
|
9
|
-
sandbox: { enabled: true },
|
|
10
|
-
allow: [
|
|
11
|
-
'Bash(npm run *)',
|
|
12
|
-
'Bash(npm test *)',
|
|
13
|
-
'Bash(npx *)',
|
|
14
|
-
'Bash(npm install)',
|
|
15
|
-
'Bash(node *)',
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
|
-
instructionFragments: {
|
|
19
|
-
node_code_patterns: {
|
|
20
|
-
content: [
|
|
21
|
-
'This is a Node.js project.',
|
|
22
|
-
'Use ESM module syntax (import/export). All relative imports must use .js extensions.',
|
|
23
|
-
'Prefer named exports over default exports.',
|
|
24
|
-
'Use TypeScript strict mode when tsconfig.json is present.',
|
|
25
|
-
].join('\n'),
|
|
26
|
-
requires_capabilities: ['read_files'],
|
|
27
|
-
},
|
|
28
|
-
node_development: {
|
|
29
|
-
content: [
|
|
30
|
-
'Install dependencies with `npm install`.',
|
|
31
|
-
'Use `npm run <script>` to execute package.json scripts.',
|
|
32
|
-
'Prefer async/await over raw Promises or callbacks.',
|
|
33
|
-
'Handle errors at system boundaries. Use typed error classes where the project defines them.',
|
|
34
|
-
].join('\n'),
|
|
35
|
-
requires_capabilities: ['write_files'],
|
|
36
|
-
},
|
|
37
|
-
node_testing: {
|
|
38
|
-
content: [
|
|
39
|
-
'Run tests with `npm test`.',
|
|
40
|
-
'Run a specific test file with `npx vitest run <path>` (vitest) or `npx jest <path>` (jest).',
|
|
41
|
-
'Always run tests after making changes to verify nothing broke.',
|
|
42
|
-
'Follow existing test patterns: check the tests/ directory for conventions before writing new tests.',
|
|
43
|
-
].join('\n'),
|
|
44
|
-
requires_capabilities: ['execute', 'write_files'],
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
python: {
|
|
49
|
-
id: 'python',
|
|
50
|
-
description: 'Python environment, auto-detected via pyproject.toml or requirements.txt',
|
|
51
|
-
detect: (cwd) => existsSync(join(cwd, 'pyproject.toml')) ||
|
|
52
|
-
existsSync(join(cwd, 'requirements.txt')) ||
|
|
53
|
-
existsSync(join(cwd, 'setup.py')),
|
|
54
|
-
policyRules: {
|
|
55
|
-
sandbox: { enabled: true },
|
|
56
|
-
allow: [
|
|
57
|
-
'Bash(pytest *)',
|
|
58
|
-
'Bash(python -m pytest *)',
|
|
59
|
-
'Bash(uv run *)',
|
|
60
|
-
'Bash(poetry run *)',
|
|
61
|
-
'Bash(python *)',
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
instructionFragments: {
|
|
65
|
-
python_code_patterns: {
|
|
66
|
-
content: [
|
|
67
|
-
'This is a Python project.',
|
|
68
|
-
'Follow PEP 8 style conventions.',
|
|
69
|
-
'Use type hints for function signatures and class attributes.',
|
|
70
|
-
'Prefer pathlib.Path over os.path for file operations.',
|
|
71
|
-
].join('\n'),
|
|
72
|
-
requires_capabilities: ['read_files'],
|
|
73
|
-
},
|
|
74
|
-
python_development: {
|
|
75
|
-
content: [
|
|
76
|
-
'If using poetry: `poetry install` and `poetry run <cmd>`. If using uv: `uv sync` and `uv run <cmd>`.',
|
|
77
|
-
'Otherwise use pip and virtualenv.',
|
|
78
|
-
'Use structured logging (logging module) instead of print statements.',
|
|
79
|
-
'Handle exceptions with specific types, not bare except clauses.',
|
|
80
|
-
].join('\n'),
|
|
81
|
-
requires_capabilities: ['write_files'],
|
|
82
|
-
},
|
|
83
|
-
python_testing: {
|
|
84
|
-
content: [
|
|
85
|
-
'Run tests with `pytest`. If using poetry or uv, prefix with `poetry run` or `uv run`.',
|
|
86
|
-
'Run a specific test: `pytest <path>::<test_name>`.',
|
|
87
|
-
'Always run tests after changes. Follow existing test patterns in the tests/ directory.',
|
|
88
|
-
'Use fixtures for shared setup. Prefer parametrize for similar test cases.',
|
|
89
|
-
].join('\n'),
|
|
90
|
-
requires_capabilities: ['execute', 'write_files'],
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
};
|
|
1
|
+
// Environment registry — delegates to ResourceLoader (YAML is the sole source).
|
|
2
|
+
import { builtinResourceLoader } from './resource-loader.js';
|
|
3
|
+
export function isEnvironmentId(value) {
|
|
4
|
+
return builtinResourceLoader.hasEnvironment(value);
|
|
5
|
+
}
|
|
95
6
|
export function getEnvironment(id) {
|
|
96
|
-
|
|
7
|
+
const env = builtinResourceLoader.getEnvironment(id);
|
|
8
|
+
if (!env)
|
|
9
|
+
throw new Error(`Unknown environment "${id}"`);
|
|
10
|
+
return env;
|
|
97
11
|
}
|
|
98
12
|
export function listEnvironments() {
|
|
99
|
-
return
|
|
13
|
+
return builtinResourceLoader.listEnvironments();
|
|
100
14
|
}
|
|
101
15
|
export function detectEnvironments(cwd) {
|
|
102
|
-
return
|
|
103
|
-
.filter((env) => env.detect(cwd))
|
|
104
|
-
.map((env) => env.id);
|
|
16
|
+
return builtinResourceLoader.detectEnvironments(cwd);
|
|
105
17
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// ResourceLoader — scans directories for YAML resource files and registers them.
|
|
2
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { parse } from 'yaml';
|
|
6
|
+
import { parseEnvironmentYaml, environmentYamlToDef } from './environment-schema.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const BUILTIN_ENVIRONMENTS_DIR = join(__dirname, '../../templates/environments');
|
|
10
|
+
/** Check whether an environment matches the given cwd. */
|
|
11
|
+
function matchesEnv(env, cwd) {
|
|
12
|
+
return env.detect ? env.detect(cwd) : false;
|
|
13
|
+
}
|
|
14
|
+
export class ResourceLoader {
|
|
15
|
+
environments = new Map();
|
|
16
|
+
loadedDirs = new Set();
|
|
17
|
+
/** Load all *.yaml files from a directory as environment definitions.
|
|
18
|
+
* Each directory is only loaded once — adding files after the first call has no effect. */
|
|
19
|
+
loadEnvironmentsFromDir(dir, allowOverride = false) {
|
|
20
|
+
if (this.loadedDirs.has(dir))
|
|
21
|
+
return;
|
|
22
|
+
this.loadedDirs.add(dir);
|
|
23
|
+
let files;
|
|
24
|
+
try {
|
|
25
|
+
files = readdirSync(dir).filter((f) => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return; // Directory does not exist or is inaccessible
|
|
29
|
+
}
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const filePath = join(dir, file);
|
|
32
|
+
try {
|
|
33
|
+
const raw = parse(readFileSync(filePath, 'utf-8'));
|
|
34
|
+
const yaml = parseEnvironmentYaml(raw);
|
|
35
|
+
const def = environmentYamlToDef(yaml);
|
|
36
|
+
if (this.environments.has(def.id) && !allowOverride)
|
|
37
|
+
continue;
|
|
38
|
+
this.environments.set(def.id, def);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (allowOverride) {
|
|
42
|
+
// User-defined file — surface the error so the user can fix it
|
|
43
|
+
process.stderr.write(`[agentforge] Warning: skipping "${filePath}": ${err instanceof Error ? err.message : String(err)}\n`);
|
|
44
|
+
}
|
|
45
|
+
// Builtin files should never fail — skip silently
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Load user-defined resources from a project's .agentforge/ directory. */
|
|
50
|
+
loadUserResources(projectDir) {
|
|
51
|
+
const envDir = join(projectDir, '.agentforge', 'environments');
|
|
52
|
+
this.loadEnvironmentsFromDir(envDir, true);
|
|
53
|
+
}
|
|
54
|
+
hasEnvironment(id) {
|
|
55
|
+
return this.environments.has(id);
|
|
56
|
+
}
|
|
57
|
+
getEnvironment(id) {
|
|
58
|
+
return this.environments.get(id);
|
|
59
|
+
}
|
|
60
|
+
listEnvironments() {
|
|
61
|
+
return [...this.environments.values()];
|
|
62
|
+
}
|
|
63
|
+
listEnvironmentIds() {
|
|
64
|
+
return [...this.environments.keys()];
|
|
65
|
+
}
|
|
66
|
+
detectEnvironments(cwd) {
|
|
67
|
+
return this.listEnvironments()
|
|
68
|
+
.filter((env) => matchesEnv(env, cwd))
|
|
69
|
+
.map((env) => env.id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Singleton — loads builtin environments from templates/environments/
|
|
73
|
+
function createBuiltinLoader() {
|
|
74
|
+
const loader = new ResourceLoader();
|
|
75
|
+
loader.loadEnvironmentsFromDir(BUILTIN_ENVIRONMENTS_DIR);
|
|
76
|
+
return loader;
|
|
77
|
+
}
|
|
78
|
+
export const builtinResourceLoader = createBuiltinLoader();
|
package/dist/registry/types.js
CHANGED
|
@@ -14,7 +14,74 @@ export const CAPABILITY_IDS = [
|
|
|
14
14
|
export function isCapability(value) {
|
|
15
15
|
return CAPABILITY_IDS.includes(value);
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// --- Capability Trait (named bundle of capabilities) ---
|
|
18
|
+
export const BUILTIN_CAPABILITY_TRAIT_IDS = [
|
|
19
|
+
'base-read',
|
|
20
|
+
'file-authoring',
|
|
21
|
+
'command-execution',
|
|
22
|
+
'web-research',
|
|
23
|
+
'delegation',
|
|
24
|
+
'interaction',
|
|
25
|
+
'notebook-editing',
|
|
26
|
+
'no-file-edits',
|
|
27
|
+
'no-commands',
|
|
28
|
+
'no-web',
|
|
29
|
+
'full-access',
|
|
30
|
+
];
|
|
31
|
+
// --- Policy Fragment ---
|
|
32
|
+
export const BUILTIN_POLICY_FRAGMENT_IDS = [
|
|
33
|
+
'allow-git-read',
|
|
34
|
+
'allow-git-write',
|
|
35
|
+
'ask-git-push',
|
|
36
|
+
'deny-destructive-shell',
|
|
37
|
+
'deny-network-downloads',
|
|
38
|
+
'deny-dynamic-exec',
|
|
39
|
+
'deny-env-files',
|
|
40
|
+
'sandbox-default',
|
|
41
|
+
];
|
|
42
|
+
export const BUILTIN_INSTRUCTION_FRAGMENT_IDS = [
|
|
43
|
+
'coordination-core',
|
|
44
|
+
'delegate-first',
|
|
45
|
+
'planning-core',
|
|
46
|
+
'planning-read-only',
|
|
47
|
+
'research-core',
|
|
48
|
+
'research-citation',
|
|
49
|
+
'research-no-file-edits',
|
|
50
|
+
'development-core',
|
|
51
|
+
'development-workflow',
|
|
52
|
+
'tester-core',
|
|
53
|
+
'tester-read-only',
|
|
54
|
+
'review-core',
|
|
55
|
+
'review-feedback',
|
|
56
|
+
'security-audit-core',
|
|
57
|
+
'security-audit-severity',
|
|
58
|
+
'research-handoff',
|
|
59
|
+
'secure-planning',
|
|
60
|
+
'secure-development',
|
|
61
|
+
'secure-development-tests',
|
|
62
|
+
'security-review-gate',
|
|
63
|
+
'post-audit-review',
|
|
64
|
+
'solo-dev-core',
|
|
65
|
+
'solo-dev-workflow',
|
|
66
|
+
'solo-dev-style',
|
|
67
|
+
'feature-orchestrator-workflow',
|
|
68
|
+
'feature-orchestrator-output',
|
|
69
|
+
'feature-planner-workflow',
|
|
70
|
+
'feature-planner-read-only',
|
|
71
|
+
'feature-developer-core',
|
|
72
|
+
'feature-developer-workflow',
|
|
73
|
+
'feature-developer-summary',
|
|
74
|
+
'feature-reviewer-checklist',
|
|
75
|
+
'feature-reviewer-style',
|
|
76
|
+
'research-orchestrator-core',
|
|
77
|
+
'research-orchestrator-workflow',
|
|
78
|
+
'research-orchestrator-output',
|
|
79
|
+
'research-planner-core',
|
|
80
|
+
'research-planner-constraints',
|
|
81
|
+
'research-developer-core',
|
|
82
|
+
'research-developer-tests',
|
|
83
|
+
'secure-orchestrator-core',
|
|
84
|
+
'secure-orchestrator-workflow',
|
|
85
|
+
'secure-orchestrator-gate',
|
|
86
|
+
'post-audit-review-core',
|
|
87
|
+
];
|
|
@@ -5,7 +5,7 @@ function getSkillBasePath(skillId) {
|
|
|
5
5
|
}
|
|
6
6
|
// --- Frontmatter (Codex: name + description only) ---
|
|
7
7
|
function buildFrontmatter(name, description) {
|
|
8
|
-
return ['---', `name: ${name}`, `description: ${description}`, '---'].join('\n');
|
|
8
|
+
return ['---', `name: ${JSON.stringify(name)}`, `description: ${JSON.stringify(description)}`, '---'].join('\n');
|
|
9
9
|
}
|
|
10
10
|
// --- Stub for unknown skills ---
|
|
11
11
|
function generateSkillStub(skillName) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isCapabilityTraitName } from '../../registry/traits.js';
|
|
2
2
|
import { isPolicyFragmentId } from '../../registry/policy-fragments.js';
|
|
3
3
|
import { isInstructionFragmentId } from '../../registry/instruction-fragments.js';
|
|
4
|
-
import { isEnvironmentId } from '../../registry/
|
|
4
|
+
import { isEnvironmentId } from '../../registry/environments.js';
|
|
5
5
|
import { getManifestTargetEntries } from '../../manifest/targets.js';
|
|
6
6
|
/**
|
|
7
7
|
* Pre-normalization manifest-level registry checks.
|
|
@@ -66,5 +66,24 @@ export function checkTeamGraphEnhanced(team) {
|
|
|
66
66
|
message: `Multiple root agents detected: ${roots.join(', ')} — consider a single orchestrator entry point`,
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
+
// HANDOFF_CAPABILITY_MISMATCH — delegating to an agent with no tools
|
|
70
|
+
for (const [agentId, agent] of agentEntries) {
|
|
71
|
+
for (const target of agent.metadata?.handoffs ?? []) {
|
|
72
|
+
const targetAgent = team.agents[target];
|
|
73
|
+
if (!targetAgent)
|
|
74
|
+
continue; // already caught by checkHandoffGraph
|
|
75
|
+
const targetTools = targetAgent.runtime.tools ?? [];
|
|
76
|
+
if (targetTools.length === 0) {
|
|
77
|
+
results.push({
|
|
78
|
+
severity: 'warning',
|
|
79
|
+
category: 'Team graph',
|
|
80
|
+
code: 'HANDOFF_CAPABILITY_MISMATCH',
|
|
81
|
+
phase: 'team-graph',
|
|
82
|
+
message: `Agent "${agentId}" hands off to "${target}" but "${target}" has no capabilities — delegation may be ineffective`,
|
|
83
|
+
agent: agentId,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
69
88
|
return results;
|
|
70
89
|
}
|
package/dist/validator/index.js
CHANGED
|
@@ -15,8 +15,8 @@ import { checkSkillRequirements } from './checks/skill-requirements.js';
|
|
|
15
15
|
import { checkMcpServers } from './checks/mcp.js';
|
|
16
16
|
import { checkTeamGraphEnhanced } from './checks/team-graph-enhanced.js';
|
|
17
17
|
const CHECKERS = (skillMap, targetName) => [
|
|
18
|
-
checkRegistryReferences,
|
|
19
|
-
checkEnvironments,
|
|
18
|
+
checkRegistryReferences, // Phase 1
|
|
19
|
+
checkEnvironments, // Phase 9
|
|
20
20
|
checkTraitCapabilities, // Phase 2
|
|
21
21
|
(team) => checkCapabilityTools(team, skillMap), // Phase 3
|
|
22
22
|
(team) => checkHandoffGraph(team, skillMap),
|
package/dist/wizard/index.js
CHANGED
|
@@ -76,7 +76,8 @@ export async function runWizard(options) {
|
|
|
76
76
|
printCommandSuccess(`Agent team initialized for project "${rawManifest.project.name}"`);
|
|
77
77
|
printManifestValidation(validation);
|
|
78
78
|
printNextSteps([
|
|
79
|
+
`Open ${chalk.bold('teamcast.yaml')} and fill in agent instructions based on ${chalk.yellow('// TODO')} comments`,
|
|
79
80
|
`${chalk.bold('teamcast explain')} - view the team structure`,
|
|
80
|
-
`
|
|
81
|
+
`Run ${chalk.bold('teamcast generate')} to apply your changes`,
|
|
81
82
|
]);
|
|
82
83
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { detectEnvironments, listEnvironments } from '../../registry/environments.js';
|
|
3
|
-
import { isEnvironmentId } from '../../registry/
|
|
3
|
+
import { isEnvironmentId } from '../../registry/environments.js';
|
|
4
4
|
import { promptCheckbox } from '../../utils/prompts.js';
|
|
5
5
|
function mergeEnvironmentIds(...lists) {
|
|
6
6
|
return [...new Set(lists.flatMap((list) => list ?? []))].filter(isEnvironmentId);
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
id: docker
|
|
2
|
+
description: "Docker environment, auto-detected via Dockerfile"
|
|
3
|
+
detect_files:
|
|
4
|
+
- Dockerfile
|
|
5
|
+
- docker-compose.yml
|
|
6
|
+
- docker-compose.yaml
|
|
7
|
+
- compose.yml
|
|
8
|
+
- compose.yaml
|
|
9
|
+
policy_rules:
|
|
10
|
+
sandbox:
|
|
11
|
+
enabled: true
|
|
12
|
+
allow:
|
|
13
|
+
- "Bash(docker build *)"
|
|
14
|
+
- "Bash(docker run *)"
|
|
15
|
+
- "Bash(docker compose *)"
|
|
16
|
+
- "Bash(docker-compose *)"
|
|
17
|
+
- "Bash(docker ps *)"
|
|
18
|
+
- "Bash(docker logs *)"
|
|
19
|
+
- "Bash(docker images *)"
|
|
20
|
+
instruction_fragments:
|
|
21
|
+
docker_patterns:
|
|
22
|
+
content: |
|
|
23
|
+
This project uses Docker.
|
|
24
|
+
Use multi-stage builds to minimize image size.
|
|
25
|
+
Prefer alpine or slim base images where practical.
|
|
26
|
+
Use .dockerignore to exclude unnecessary files from build context.
|
|
27
|
+
requires_capabilities:
|
|
28
|
+
- read_files
|
|
29
|
+
docker_development:
|
|
30
|
+
content: |
|
|
31
|
+
Build images with `docker build -t <tag> .`.
|
|
32
|
+
Use `docker compose up` for multi-container setups.
|
|
33
|
+
Pin base image versions for reproducible builds.
|
|
34
|
+
Order Dockerfile instructions to maximize layer caching (dependencies before source code).
|
|
35
|
+
requires_capabilities:
|
|
36
|
+
- write_files
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: go
|
|
2
|
+
description: "Go environment, auto-detected via go.mod"
|
|
3
|
+
detect_files:
|
|
4
|
+
- go.mod
|
|
5
|
+
policy_rules:
|
|
6
|
+
sandbox:
|
|
7
|
+
enabled: true
|
|
8
|
+
allow:
|
|
9
|
+
- "Bash(go build *)"
|
|
10
|
+
- "Bash(go test *)"
|
|
11
|
+
- "Bash(go run *)"
|
|
12
|
+
- "Bash(go vet *)"
|
|
13
|
+
- "Bash(go mod *)"
|
|
14
|
+
- "Bash(go generate *)"
|
|
15
|
+
instruction_fragments:
|
|
16
|
+
go_code_patterns:
|
|
17
|
+
content: |
|
|
18
|
+
This is a Go project.
|
|
19
|
+
Follow standard Go conventions and idiomatic patterns.
|
|
20
|
+
Use gofmt/goimports for formatting.
|
|
21
|
+
Prefer short variable names in small scopes, descriptive names in larger scopes.
|
|
22
|
+
requires_capabilities:
|
|
23
|
+
- read_files
|
|
24
|
+
go_development:
|
|
25
|
+
content: |
|
|
26
|
+
Use `go build ./...` to compile all packages.
|
|
27
|
+
Use `go mod tidy` to clean up dependencies.
|
|
28
|
+
Handle errors explicitly — do not ignore returned errors.
|
|
29
|
+
Prefer returning errors over panicking.
|
|
30
|
+
requires_capabilities:
|
|
31
|
+
- write_files
|
|
32
|
+
go_testing:
|
|
33
|
+
content: |
|
|
34
|
+
Run tests with `go test ./...`.
|
|
35
|
+
Run a specific test: `go test -run TestName ./path/to/package`.
|
|
36
|
+
Use table-driven tests for multiple cases.
|
|
37
|
+
Always run tests after changes to verify nothing broke.
|
|
38
|
+
requires_capabilities:
|
|
39
|
+
- execute
|
|
40
|
+
- write_files
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
id: java
|
|
2
|
+
description: "Java environment, auto-detected via pom.xml or build.gradle"
|
|
3
|
+
detect_files:
|
|
4
|
+
- pom.xml
|
|
5
|
+
- build.gradle
|
|
6
|
+
- build.gradle.kts
|
|
7
|
+
policy_rules:
|
|
8
|
+
sandbox:
|
|
9
|
+
enabled: true
|
|
10
|
+
allow:
|
|
11
|
+
- "Bash(mvn *)"
|
|
12
|
+
- "Bash(gradle *)"
|
|
13
|
+
- "Bash(./gradlew *)"
|
|
14
|
+
- "Bash(java *)"
|
|
15
|
+
- "Bash(javac *)"
|
|
16
|
+
instruction_fragments:
|
|
17
|
+
java_code_patterns:
|
|
18
|
+
content: |
|
|
19
|
+
This is a Java project.
|
|
20
|
+
Follow standard Java naming conventions (camelCase for methods, PascalCase for classes).
|
|
21
|
+
Use appropriate access modifiers.
|
|
22
|
+
Prefer composition over inheritance where practical.
|
|
23
|
+
requires_capabilities:
|
|
24
|
+
- read_files
|
|
25
|
+
java_development:
|
|
26
|
+
content: |
|
|
27
|
+
If using Maven: `mvn compile` to build, `mvn package` to create artifacts.
|
|
28
|
+
If using Gradle: `./gradlew build` or `gradle build`.
|
|
29
|
+
Handle exceptions with specific types, not bare catch blocks.
|
|
30
|
+
Use try-with-resources for AutoCloseable resources.
|
|
31
|
+
requires_capabilities:
|
|
32
|
+
- write_files
|
|
33
|
+
java_testing:
|
|
34
|
+
content: |
|
|
35
|
+
Run tests with `mvn test` (Maven) or `./gradlew test` (Gradle).
|
|
36
|
+
Run a specific test: `mvn -Dtest=TestClassName test` or `./gradlew test --tests TestClassName`.
|
|
37
|
+
Use JUnit 5 annotations. Follow existing test patterns in the project.
|
|
38
|
+
Always run tests after changes to verify nothing broke.
|
|
39
|
+
requires_capabilities:
|
|
40
|
+
- execute
|
|
41
|
+
- write_files
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
id: node
|
|
2
|
+
description: "Node.js environment, auto-detected via package.json"
|
|
3
|
+
detect_files:
|
|
4
|
+
- package.json
|
|
5
|
+
policy_rules:
|
|
6
|
+
sandbox:
|
|
7
|
+
enabled: true
|
|
8
|
+
allow:
|
|
9
|
+
- "Bash(npm run *)"
|
|
10
|
+
- "Bash(npm test *)"
|
|
11
|
+
- "Bash(npx *)"
|
|
12
|
+
- "Bash(npm install)"
|
|
13
|
+
- "Bash(node *)"
|
|
14
|
+
instruction_fragments:
|
|
15
|
+
node_code_patterns:
|
|
16
|
+
content: |
|
|
17
|
+
This is a Node.js project.
|
|
18
|
+
Use ESM module syntax (import/export). All relative imports must use .js extensions.
|
|
19
|
+
Prefer named exports over default exports.
|
|
20
|
+
Use TypeScript strict mode when tsconfig.json is present.
|
|
21
|
+
requires_capabilities:
|
|
22
|
+
- read_files
|
|
23
|
+
node_development:
|
|
24
|
+
content: |
|
|
25
|
+
Install dependencies with `npm install`.
|
|
26
|
+
Use `npm run <script>` to execute package.json scripts.
|
|
27
|
+
Prefer async/await over raw Promises or callbacks.
|
|
28
|
+
Handle errors at system boundaries. Use typed error classes where the project defines them.
|
|
29
|
+
requires_capabilities:
|
|
30
|
+
- write_files
|
|
31
|
+
node_testing:
|
|
32
|
+
content: |
|
|
33
|
+
Run tests with `npm test`.
|
|
34
|
+
Run a specific test file with `npx vitest run <path>` (vitest) or `npx jest <path>` (jest).
|
|
35
|
+
Always run tests after making changes to verify nothing broke.
|
|
36
|
+
Follow existing test patterns: check the tests/ directory for conventions before writing new tests.
|
|
37
|
+
requires_capabilities:
|
|
38
|
+
- execute
|
|
39
|
+
- write_files
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
id: python
|
|
2
|
+
description: "Python environment, auto-detected via pyproject.toml or requirements.txt"
|
|
3
|
+
detect_files:
|
|
4
|
+
- pyproject.toml
|
|
5
|
+
- requirements.txt
|
|
6
|
+
- setup.py
|
|
7
|
+
policy_rules:
|
|
8
|
+
sandbox:
|
|
9
|
+
enabled: true
|
|
10
|
+
allow:
|
|
11
|
+
- "Bash(pytest *)"
|
|
12
|
+
- "Bash(python -m pytest *)"
|
|
13
|
+
- "Bash(uv run *)"
|
|
14
|
+
- "Bash(poetry run *)"
|
|
15
|
+
- "Bash(python *)"
|
|
16
|
+
instruction_fragments:
|
|
17
|
+
python_code_patterns:
|
|
18
|
+
content: |
|
|
19
|
+
This is a Python project.
|
|
20
|
+
Follow PEP 8 style conventions.
|
|
21
|
+
Use type hints for function signatures and class attributes.
|
|
22
|
+
Prefer pathlib.Path over os.path for file operations.
|
|
23
|
+
requires_capabilities:
|
|
24
|
+
- read_files
|
|
25
|
+
python_development:
|
|
26
|
+
content: |
|
|
27
|
+
If using poetry: `poetry install` and `poetry run <cmd>`. If using uv: `uv sync` and `uv run <cmd>`.
|
|
28
|
+
Otherwise use pip and virtualenv.
|
|
29
|
+
Use structured logging (logging module) instead of print statements.
|
|
30
|
+
Handle exceptions with specific types, not bare except clauses.
|
|
31
|
+
requires_capabilities:
|
|
32
|
+
- write_files
|
|
33
|
+
python_testing:
|
|
34
|
+
content: |
|
|
35
|
+
Run tests with `pytest`. If using poetry or uv, prefix with `poetry run` or `uv run`.
|
|
36
|
+
Run a specific test: `pytest <path>::<test_name>`.
|
|
37
|
+
Always run tests after changes. Follow existing test patterns in the tests/ directory.
|
|
38
|
+
Use fixtures for shared setup. Prefer parametrize for similar test cases.
|
|
39
|
+
requires_capabilities:
|
|
40
|
+
- execute
|
|
41
|
+
- write_files
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
id: ruby
|
|
2
|
+
description: "Ruby environment, auto-detected via Gemfile"
|
|
3
|
+
detect_files:
|
|
4
|
+
- Gemfile
|
|
5
|
+
policy_rules:
|
|
6
|
+
sandbox:
|
|
7
|
+
enabled: true
|
|
8
|
+
allow:
|
|
9
|
+
- "Bash(bundle *)"
|
|
10
|
+
- "Bash(rake *)"
|
|
11
|
+
- "Bash(rspec *)"
|
|
12
|
+
- "Bash(ruby *)"
|
|
13
|
+
- "Bash(rails *)"
|
|
14
|
+
instruction_fragments:
|
|
15
|
+
ruby_code_patterns:
|
|
16
|
+
content: |
|
|
17
|
+
This is a Ruby project.
|
|
18
|
+
Follow Ruby style conventions (snake_case for methods/variables, PascalCase for classes).
|
|
19
|
+
Use frozen string literal comments where the project follows that convention.
|
|
20
|
+
Prefer blocks and enumerators over manual loops.
|
|
21
|
+
requires_capabilities:
|
|
22
|
+
- read_files
|
|
23
|
+
ruby_development:
|
|
24
|
+
content: |
|
|
25
|
+
Install dependencies with `bundle install`.
|
|
26
|
+
Use `bundle exec` to run commands in the context of the bundle.
|
|
27
|
+
Prefer keyword arguments for methods with multiple optional parameters.
|
|
28
|
+
Handle errors with specific exception classes.
|
|
29
|
+
requires_capabilities:
|
|
30
|
+
- write_files
|
|
31
|
+
ruby_testing:
|
|
32
|
+
content: |
|
|
33
|
+
Run tests with `bundle exec rspec` (RSpec) or `bundle exec rake test` (Minitest).
|
|
34
|
+
Run a specific test: `bundle exec rspec path/to/spec.rb:LINE`.
|
|
35
|
+
Follow existing test patterns. Use shared examples for reusable test behaviors.
|
|
36
|
+
Always run tests after changes to verify nothing broke.
|
|
37
|
+
requires_capabilities:
|
|
38
|
+
- execute
|
|
39
|
+
- write_files
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
id: rust
|
|
2
|
+
description: "Rust environment, auto-detected via Cargo.toml"
|
|
3
|
+
detect_files:
|
|
4
|
+
- Cargo.toml
|
|
5
|
+
policy_rules:
|
|
6
|
+
sandbox:
|
|
7
|
+
enabled: true
|
|
8
|
+
allow:
|
|
9
|
+
- "Bash(cargo build *)"
|
|
10
|
+
- "Bash(cargo test *)"
|
|
11
|
+
- "Bash(cargo run *)"
|
|
12
|
+
- "Bash(cargo clippy *)"
|
|
13
|
+
- "Bash(cargo fmt *)"
|
|
14
|
+
- "Bash(cargo check *)"
|
|
15
|
+
- "Bash(rustfmt *)"
|
|
16
|
+
instruction_fragments:
|
|
17
|
+
rust_code_patterns:
|
|
18
|
+
content: |
|
|
19
|
+
This is a Rust project.
|
|
20
|
+
Follow Rust idioms: prefer ownership over borrowing when practical.
|
|
21
|
+
Use clippy lints to catch common mistakes.
|
|
22
|
+
Prefer Result/Option over panicking.
|
|
23
|
+
requires_capabilities:
|
|
24
|
+
- read_files
|
|
25
|
+
rust_development:
|
|
26
|
+
content: |
|
|
27
|
+
Use `cargo build` to compile the project.
|
|
28
|
+
Use `cargo check` for fast feedback without full compilation.
|
|
29
|
+
Run `cargo clippy` before committing to catch lint issues.
|
|
30
|
+
Use `cargo fmt` to format code consistently.
|
|
31
|
+
requires_capabilities:
|
|
32
|
+
- write_files
|
|
33
|
+
rust_testing:
|
|
34
|
+
content: |
|
|
35
|
+
Run tests with `cargo test`.
|
|
36
|
+
Run a specific test: `cargo test test_name`.
|
|
37
|
+
Use `#[cfg(test)]` modules for unit tests within source files.
|
|
38
|
+
Always run tests after changes to verify nothing broke.
|
|
39
|
+
requires_capabilities:
|
|
40
|
+
- execute
|
|
41
|
+
- write_files
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
id: terraform
|
|
2
|
+
description: "Terraform environment, auto-detected via main.tf"
|
|
3
|
+
detect_files:
|
|
4
|
+
- main.tf
|
|
5
|
+
- terraform.tf
|
|
6
|
+
policy_rules:
|
|
7
|
+
sandbox:
|
|
8
|
+
enabled: true
|
|
9
|
+
allow:
|
|
10
|
+
- "Bash(terraform init *)"
|
|
11
|
+
- "Bash(terraform plan *)"
|
|
12
|
+
- "Bash(terraform validate *)"
|
|
13
|
+
- "Bash(terraform fmt *)"
|
|
14
|
+
- "Bash(terraform state *)"
|
|
15
|
+
instruction_fragments:
|
|
16
|
+
terraform_patterns:
|
|
17
|
+
content: |
|
|
18
|
+
This project uses Terraform for infrastructure as code.
|
|
19
|
+
Follow HCL conventions: use snake_case for resource names and variables.
|
|
20
|
+
Organize configuration into logical files (main.tf, variables.tf, outputs.tf).
|
|
21
|
+
Use modules for reusable infrastructure components.
|
|
22
|
+
requires_capabilities:
|
|
23
|
+
- read_files
|
|
24
|
+
terraform_development:
|
|
25
|
+
content: |
|
|
26
|
+
Run `terraform init` to initialize providers and modules.
|
|
27
|
+
Run `terraform plan` to preview changes before applying.
|
|
28
|
+
Run `terraform validate` and `terraform fmt` before committing.
|
|
29
|
+
Never apply changes without reviewing the plan first.
|
|
30
|
+
requires_capabilities:
|
|
31
|
+
- write_files
|