contextos-agents 2.0.0 → 2.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/.agents/AGENTS.md +53 -33
- package/.agents/adapters/aider/export.js +41 -14
- package/.agents/adapters/claude/export.js +1 -1
- package/.agents/adapters/copilot/export.js +1 -1
- package/.agents/adapters/cursor/export.js +1 -1
- package/.agents/adapters/drift-detector.js +80 -7
- package/.agents/adapters/gemini/export.js +1 -1
- package/.agents/adapters/pure-compiler.js +10 -0
- package/.agents/adapters/shared.js +13 -4
- package/.agents/adapters/zed/export.js +1 -1
- package/.agents/compiled/registry.v2.json +29 -25
- package/.agents/compiled/registry.v2.sha256 +1 -1
- package/.agents/core/skills/context-os/SKILL.md +34 -37
- package/.agents/core/skills/engineering-workflow/SKILL.md +24 -24
- package/.agents/core/skills/gemini-precision/EXAMPLES.md +72 -0
- package/.agents/core/skills/gemini-precision/SKILL.md +2 -1
- package/.agents/core/skills/gemini-precision/TROUBLESHOOTING.md +25 -0
- package/.agents/core/skills/gemini-precision/skill.yaml +2 -0
- package/.agents/core/skills/gstack-roles/SKILL.md +7 -6
- package/.agents/core/skills/security/SKILL.md +44 -16
- package/.agents/core/skills/security/skill.yaml +0 -1
- package/.agents/ctx.js +16 -10
- package/.agents/generated/claude/skills/context-os/SKILL.md +34 -37
- package/.agents/generated/claude/skills/engineering-workflow/SKILL.md +24 -24
- package/.agents/generated/claude/skills/gemini-precision/SKILL.md +102 -1
- package/.agents/generated/claude/skills/gstack-roles/SKILL.md +7 -6
- package/.agents/generated/claude/skills/security/SKILL.md +44 -16
- package/.agents/generated/gemini/skills/context-os/SKILL.md +34 -37
- package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +24 -24
- package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +105 -1
- package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +7 -6
- package/.agents/generated/gemini/skills/security/SKILL.md +44 -125
- package/.agents/plugins.js +5 -4
- package/.agents/resolver/canonical-resolver.js +7 -7
- package/.agents/validate.js +69 -1
- package/README.md +80 -23
- package/bin/commands/hook.js +129 -0
- package/bin/commands/scan.js +70 -0
- package/bin/commands.js +39 -1
- package/bin/index.js +144 -33
- package/bin/lib/gate.js +171 -0
- package/bin/lib/git-snapshot.js +187 -0
- package/bin/lib/scan.js +380 -0
- package/package.json +4 -2
- package/.agents/core/skills/security/security.md +0 -106
package/README.md
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/kok-o/contextos-agents">
|
|
3
|
+
<img src="./Frame%202.png" alt="ContextOS Logo" width="88" height="88" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">contextos-agents</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>One version-controlled source of engineering rules for supported coding agents.</strong>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://www.npmjs.com/package/contextos-agents"><img src="https://img.shields.io/npm/v/contextos-agents?color=18181b&logo=npm" alt="npm version" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/contextos-agents"><img src="https://img.shields.io/npm/dt/contextos-agents?color=18181b&logo=npm&label=downloads" alt="npm downloads" /></a>
|
|
16
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D22.0.0-18181b?logo=node.js" alt="Node.js" /></a>
|
|
17
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache_2.0-18181b" alt="License" /></a>
|
|
18
|
+
<a href="https://github.com/kok-o/contextos-agents/actions/workflows/validate-skills.yml"><img src="https://img.shields.io/github/actions/workflow/status/kok-o/contextos-agents/validate-skills.yml?label=ci&color=18181b&logo=github" alt="CI" /></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="#installation">Installation</a> •
|
|
23
|
+
<a href="./GUIDE.md">Guide</a> •
|
|
24
|
+
<a href="./docs/product/onboarding.md">Onboarding</a> •
|
|
25
|
+
<a href="./docs/ADAPTER_COMPATIBILITY.md">Adapters</a> •
|
|
26
|
+
<a href="#supported-agents--compilation">Supported Agents</a> •
|
|
27
|
+
<a href="./CONTRIBUTING.md">Contributing</a> •
|
|
28
|
+
<a href="https://www.npmjs.com/package/contextos-agents">npm</a>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
ContextOS is a deterministic context and policy compiler for AI coding agents. It transforms your team's version-controlled engineering rules into focused, verifiable context for Gemini, Claude Code, Cursor, GitHub Copilot, Aider, and Zed - and detects configuration drift in CI.
|
|
11
34
|
|
|
12
35
|
## Installation
|
|
13
36
|
|
|
@@ -34,22 +57,24 @@ npx contextos-agents --skip-compile # Skip auto-compilation step
|
|
|
34
57
|
|
|
35
58
|
## Why ContextOS?
|
|
36
59
|
|
|
37
|
-
|
|
60
|
+
Modern development teams face fragmented AI tooling: engineers use Cursor, Claude Code, GitHub Copilot, Gemini, Zed, and Aider. Each tool requires its own proprietary rules format, leading to configuration drift, contradictory standards, and unvetted AI slop (`// TODO`, leaked secrets).
|
|
38
61
|
|
|
39
|
-
|
|
62
|
+
Artificially truncating skills to save tokens degrades model reasoning and induces hallucinations. Instead, ContextOS ensures that agents receive complete, high-fidelity engineering context from a single version-controlled source.
|
|
63
|
+
|
|
64
|
+
**ContextOS is not another coding agent.** It is the deterministic context compiler and policy engine for the agents your team already uses.
|
|
40
65
|
|
|
41
66
|
### The Three Pillars
|
|
42
67
|
|
|
43
|
-
1. **Portable:** Define your engineering
|
|
44
|
-
2. **Focused:** The resolver
|
|
45
|
-
3. **Verifiable:**
|
|
68
|
+
1. **Portable (Multi-Agent):** Define your engineering skills once in standard Markdown. ContextOS compiles native configurations for all supported agents (Gemini, Claude Code, Cursor, Copilot, Aider, and Zed).
|
|
69
|
+
2. **High-Fidelity & Focused:** The resolver maps domain skills to relevant tasks without lossy truncation, delivering rich, complete context to the model.
|
|
70
|
+
3. **Verifiable in CI:** Lockfile v2 provenance, dual-hash verification, and CI quality gates detect configuration drift and enforce quality guardrails before merge.
|
|
46
71
|
|
|
47
72
|
## How it works
|
|
48
73
|
|
|
49
74
|
1. Define version-controlled engineering policies once.
|
|
50
|
-
2. Resolve
|
|
75
|
+
2. Resolve the complete, relevant skill policies for the current task.
|
|
51
76
|
3. Compile native configuration for each coding agent.
|
|
52
|
-
4. Detect configuration drift in CI.
|
|
77
|
+
4. Detect configuration drift and policy violations in CI.
|
|
53
78
|
|
|
54
79
|
```bash
|
|
55
80
|
contextos resolve "review authentication changes" \
|
|
@@ -62,10 +87,10 @@ Selected:
|
|
|
62
87
|
engineering-workflow required dependency
|
|
63
88
|
|
|
64
89
|
Excluded:
|
|
65
|
-
context-manager
|
|
90
|
+
context-manager domain relevance filter
|
|
66
91
|
|
|
67
92
|
Risk: high
|
|
68
|
-
|
|
93
|
+
Context status: complete and verified
|
|
69
94
|
|
|
70
95
|
## Dynamic Skill Resolution & Unified CLI (`contextos` / `ctx.js`)
|
|
71
96
|
|
|
@@ -108,9 +133,35 @@ contextos doctor
|
|
|
108
133
|
contextos export all # Compile for all agents
|
|
109
134
|
```
|
|
110
135
|
|
|
111
|
-
###
|
|
136
|
+
### Staged Index Security Scanner (`contextos scan`)
|
|
137
|
+
|
|
138
|
+
Scan staged changes directly from the Git index for secret leaks, blocked credential files, unfinished lazy stubs, and write-scope containment:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
contextos scan --staged --enforce
|
|
142
|
+
contextos scan --staged --placeholders --scope .agents/task-scope.json --json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Safe Git Pre-Commit Hooks (`contextos hook`)
|
|
146
|
+
|
|
147
|
+
Install or remove isolated pre-commit hooks that run fast security checks without clobbering existing developer hooks:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
contextos hook install
|
|
151
|
+
contextos hook uninstall
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### CI Quality Gate (`contextos gate`)
|
|
155
|
+
|
|
156
|
+
Run the complete 8-point production quality gate locally:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
contextos gate
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### CI Quality Gate Action (contextos-gate)
|
|
112
163
|
|
|
113
|
-
Guard your repository against skill drift,
|
|
164
|
+
Guard your repository against skill drift, missing outputs, and rule regressions using the official GitHub Composite Action:
|
|
114
165
|
|
|
115
166
|
```yaml
|
|
116
167
|
# .github/workflows/pr-gate.yml
|
|
@@ -122,8 +173,14 @@ jobs:
|
|
|
122
173
|
steps:
|
|
123
174
|
- uses: actions/checkout@v4
|
|
124
175
|
- uses: kok-o/contextos-agents/.github/actions/contextos-gate@v2.0.0
|
|
176
|
+
with:
|
|
177
|
+
version: '2.0.0' # Pinned version of contextos-agents runner
|
|
178
|
+
adapters: 'all' # Adapters to verify (or specific: 'cursor', 'claude')
|
|
179
|
+
working-directory: '.' # Project root directory
|
|
125
180
|
```
|
|
126
181
|
|
|
182
|
+
The action executes the verified ContextOS quality gate in-process from the pinned package version, verifying generated AI adapter configs against source skills without executing untrusted scripts from pull requests, and without requiring a Node.js project or running `npm test`.
|
|
183
|
+
|
|
127
184
|
## Optional MCP integration (Beta)
|
|
128
185
|
|
|
129
186
|
The MCP server is a separate beta package. It is not part of the stable `contextos-agents` core.
|
|
@@ -137,9 +194,9 @@ npx contextos-mcp --dir .
|
|
|
137
194
|
|
|
138
195
|
The MCP server is read-only by default. Runtime execution remains experimental and is outside the stable core scope.
|
|
139
196
|
|
|
140
|
-
## Security
|
|
197
|
+
## Security - Third-Party Skills
|
|
141
198
|
|
|
142
|
-
ContextOS skills are **executable context**
|
|
199
|
+
ContextOS skills are **executable context** - they become part of the system prompt that controls your AI agent's behavior. A malicious skill could instruct the AI agent to exfiltrate environment variables, modify files, or ignore your project's security policies.
|
|
143
200
|
|
|
144
201
|
> [!CAUTION]
|
|
145
202
|
> **Install skills only from repositories you trust as you would trust executable code.** Skills installed via `ctx.js skill add` from npm or GitHub are not sandboxed.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bin/commands/hook.js
|
|
3
|
+
* ContextOS Git Hook Installer & Lifecycle Manager
|
|
4
|
+
*
|
|
5
|
+
* Safely installs and uninstalls the pre-commit governance hook.
|
|
6
|
+
* Preserves pre-existing user hooks, supports core.hooksPath and worktrees,
|
|
7
|
+
* and operates idempotently.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { findHooksDir } = require('../lib/git-snapshot.js');
|
|
15
|
+
|
|
16
|
+
const HOOK_MARKER_START = '# BEGIN CONTEXTOS HOOK';
|
|
17
|
+
const HOOK_MARKER_END = '# END CONTEXTOS HOOK';
|
|
18
|
+
|
|
19
|
+
const HOOK_PAYLOAD = `${HOOK_MARKER_START}
|
|
20
|
+
npx contextos-agents scan --staged --enforce || exit 1
|
|
21
|
+
${HOOK_MARKER_END}`;
|
|
22
|
+
|
|
23
|
+
function installHook(cwd = process.cwd()) {
|
|
24
|
+
const hooksDir = findHooksDir(cwd);
|
|
25
|
+
|
|
26
|
+
if (!hooksDir) {
|
|
27
|
+
console.error('[ERROR] Not a Git repository or Git hooks directory not found.');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(hooksDir)) {
|
|
32
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hookFile = path.join(hooksDir, 'pre-commit');
|
|
36
|
+
|
|
37
|
+
if (fs.existsSync(hookFile)) {
|
|
38
|
+
const existing = fs.readFileSync(hookFile, 'utf8');
|
|
39
|
+
|
|
40
|
+
if (existing.includes(HOOK_MARKER_START)) {
|
|
41
|
+
// Replace existing block cleanly
|
|
42
|
+
const regex = new RegExp(`${HOOK_MARKER_START}[\\s\\S]*?${HOOK_MARKER_END}`);
|
|
43
|
+
const updated = existing.replace(regex, HOOK_PAYLOAD);
|
|
44
|
+
fs.writeFileSync(hookFile, updated, 'utf8');
|
|
45
|
+
console.log(`✓ ContextOS pre-commit hook updated in ${hookFile}`);
|
|
46
|
+
} else {
|
|
47
|
+
// Append while preserving user's existing hook script
|
|
48
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
49
|
+
const updated = existing + separator + HOOK_PAYLOAD + '\n';
|
|
50
|
+
fs.writeFileSync(hookFile, updated, 'utf8');
|
|
51
|
+
console.log(`✓ ContextOS pre-commit hook appended to existing hook in ${hookFile}`);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// Fresh hook installation with shebang
|
|
55
|
+
const fresh = `#!/bin/sh\n\n${HOOK_PAYLOAD}\n`;
|
|
56
|
+
fs.writeFileSync(hookFile, fresh, 'utf8');
|
|
57
|
+
console.log(`✓ ContextOS pre-commit hook installed in ${hookFile}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
fs.chmodSync(hookFile, 0o755);
|
|
62
|
+
} catch {
|
|
63
|
+
// Best effort on platforms that do not support POSIX file modes
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function uninstallHook(cwd = process.cwd()) {
|
|
68
|
+
const hooksDir = findHooksDir(cwd);
|
|
69
|
+
|
|
70
|
+
if (!hooksDir) {
|
|
71
|
+
console.error('[ERROR] Not a Git repository or Git hooks directory not found.');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const hookFile = path.join(hooksDir, 'pre-commit');
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(hookFile)) {
|
|
78
|
+
console.log('✓ No pre-commit hook found to uninstall.');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const existing = fs.readFileSync(hookFile, 'utf8');
|
|
83
|
+
|
|
84
|
+
if (!existing.includes(HOOK_MARKER_START)) {
|
|
85
|
+
console.log('✓ No ContextOS hook block detected in pre-commit hook.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Remove exclusively the ContextOS demarcated block
|
|
90
|
+
const regex = new RegExp(`\\n?${HOOK_MARKER_START}[\\s\\S]*?${HOOK_MARKER_END}\\n?`, 'g');
|
|
91
|
+
const remaining = existing.replace(regex, '').trim();
|
|
92
|
+
|
|
93
|
+
// If file contains only shebang or is empty, remove it completely
|
|
94
|
+
if (!remaining || remaining === '#!/bin/sh' || remaining === '#!/bin/bash') {
|
|
95
|
+
fs.unlinkSync(hookFile);
|
|
96
|
+
console.log(`✓ ContextOS pre-commit hook uninstalled and empty ${hookFile} removed.`);
|
|
97
|
+
} else {
|
|
98
|
+
// Preserve remaining user scripts
|
|
99
|
+
fs.writeFileSync(hookFile, remaining + '\n', 'utf8');
|
|
100
|
+
console.log(`✓ ContextOS block removed from ${hookFile}; user commands preserved.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hookCommand(args, flags) {
|
|
105
|
+
const subCommand = args[1] ? args[1].toLowerCase().trim() : null;
|
|
106
|
+
|
|
107
|
+
if (subCommand === 'install') {
|
|
108
|
+
installHook(process.cwd());
|
|
109
|
+
process.exit(0);
|
|
110
|
+
} else if (subCommand === 'uninstall') {
|
|
111
|
+
uninstallHook(process.cwd());
|
|
112
|
+
process.exit(0);
|
|
113
|
+
} else {
|
|
114
|
+
console.log('\nUsage: contextos hook <install|uninstall>\n');
|
|
115
|
+
console.log('Commands:');
|
|
116
|
+
console.log(' install Safely install pre-commit quality gate hook');
|
|
117
|
+
console.log(' uninstall Remove ContextOS block while preserving custom hooks\n');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
installHook,
|
|
124
|
+
uninstallHook,
|
|
125
|
+
hookCommand,
|
|
126
|
+
HOOK_MARKER_START,
|
|
127
|
+
HOOK_MARKER_END,
|
|
128
|
+
HOOK_PAYLOAD,
|
|
129
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bin/commands/scan.js
|
|
3
|
+
* ContextOS CLI Command Handler: scan
|
|
4
|
+
*
|
|
5
|
+
* Runs security, placeholder, and write-scope checks against Git staged files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const { runScan } = require('../lib/scan.js');
|
|
11
|
+
|
|
12
|
+
function scanCommand(args, flags) {
|
|
13
|
+
const isJson = flags.json || args.includes('--json');
|
|
14
|
+
const enforce = flags.enforce || args.includes('--enforce');
|
|
15
|
+
const checkPlaceholders = flags.placeholders || args.includes('--placeholders');
|
|
16
|
+
const checkSecrets = !args.includes('--no-secrets');
|
|
17
|
+
|
|
18
|
+
const scopeIdx = args.indexOf('--scope');
|
|
19
|
+
const scopeFile = scopeIdx !== -1 && args[scopeIdx + 1] ? args[scopeIdx + 1] : null;
|
|
20
|
+
|
|
21
|
+
const result = runScan({
|
|
22
|
+
cwd: process.cwd(),
|
|
23
|
+
staged: true,
|
|
24
|
+
secrets: checkSecrets,
|
|
25
|
+
placeholders: checkPlaceholders,
|
|
26
|
+
scope: scopeFile,
|
|
27
|
+
enforce,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (isJson) {
|
|
31
|
+
console.log(JSON.stringify(result, null, 2));
|
|
32
|
+
process.exit(result.code);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('\n══════════════════════════════════════════════════════');
|
|
36
|
+
console.log(' ContextOS Staged Index Scanner');
|
|
37
|
+
console.log('══════════════════════════════════════════════════════\n');
|
|
38
|
+
|
|
39
|
+
if (result.code === 2) {
|
|
40
|
+
console.error(` [ERROR] ${result.error || 'Scan incomplete'}\n`);
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(` Staged Files Scanned : ${result.stats.stagedFilesCount}`);
|
|
45
|
+
console.log(` Enforcement Mode : ${enforce ? 'STRICT (fails on findings)' : 'ADVISORY (warnings only)'}`);
|
|
46
|
+
console.log(` Violations Found : ${result.stats.violationsCount}\n`);
|
|
47
|
+
|
|
48
|
+
if (result.findings.length > 0) {
|
|
49
|
+
console.log(' Findings:');
|
|
50
|
+
for (const f of result.findings) {
|
|
51
|
+
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
52
|
+
const tag = f.severity === 'error' ? 'ERROR' : 'WARN';
|
|
53
|
+
console.log(` • [${f.ruleId}] [${tag}] ${loc}`);
|
|
54
|
+
console.log(` ${f.details}`);
|
|
55
|
+
}
|
|
56
|
+
console.log('');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('──────────────────────────────────────────────────────');
|
|
60
|
+
if (result.ok) {
|
|
61
|
+
console.log(' RESULT: PASSED (No blocking violations in staged index)');
|
|
62
|
+
} else {
|
|
63
|
+
console.log(' RESULT: FAILED (Commit blocked due to staged violations)');
|
|
64
|
+
}
|
|
65
|
+
console.log('──────────────────────────────────────────────────────\n');
|
|
66
|
+
|
|
67
|
+
process.exit(result.code);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = scanCommand;
|
package/bin/commands.js
CHANGED
|
@@ -195,13 +195,28 @@ const COMMAND_REGISTRY = {
|
|
|
195
195
|
requiresProject: true,
|
|
196
196
|
options: [],
|
|
197
197
|
},
|
|
198
|
+
gate: {
|
|
199
|
+
name: 'gate',
|
|
200
|
+
description: 'Run quality gate validation and drift checks without disk mutation',
|
|
201
|
+
usage: 'contextos gate [--project <dir>] [--target <adapters>] [--profile <name>] [--json]',
|
|
202
|
+
requiresProject: false,
|
|
203
|
+
options: [
|
|
204
|
+
{ flag: '--project <dir>', desc: 'Target project directory (default: cwd)' },
|
|
205
|
+
{ flag: '--target <adapters>', desc: 'Adapters to verify (gemini, claude, cursor, copilot, aider, zed, all)' },
|
|
206
|
+
{ flag: '--profile <name>', desc: 'Profile override for verification' },
|
|
207
|
+
{ flag: '--json', desc: 'Output gate report in versioned JSON format' },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
198
210
|
export: {
|
|
199
211
|
name: 'export',
|
|
200
212
|
description: 'Compile skills for target agent (gemini, claude, cursor, copilot, aider, zed, all)',
|
|
201
|
-
usage: 'contextos export <target> [--profile <name>]',
|
|
213
|
+
usage: 'contextos export <target> [--check] [--project <dir>] [--profile <name>] [--json]',
|
|
202
214
|
requiresProject: true,
|
|
203
215
|
options: [
|
|
216
|
+
{ flag: '--check', desc: 'Verify synchronization without modifying disk' },
|
|
217
|
+
{ flag: '--project <dir>', desc: 'Target project directory (default: cwd)' },
|
|
204
218
|
{ flag: '--profile <name>', desc: 'Apply profile for this export run' },
|
|
219
|
+
{ flag: '--json', desc: 'Output in JSON format' },
|
|
205
220
|
],
|
|
206
221
|
},
|
|
207
222
|
profile: {
|
|
@@ -293,6 +308,29 @@ const COMMAND_REGISTRY = {
|
|
|
293
308
|
{ flag: '--continue [txId]', desc: 'Attempt to resume and complete prepared transaction' },
|
|
294
309
|
],
|
|
295
310
|
},
|
|
311
|
+
scan: {
|
|
312
|
+
name: 'scan',
|
|
313
|
+
description: 'Scan Git staged index for secrets, lazy stubs, and write-scope containment',
|
|
314
|
+
usage: 'contextos scan [--staged] [--secrets] [--placeholders] [--scope <file>] [--enforce] [--json]',
|
|
315
|
+
requiresProject: false,
|
|
316
|
+
options: [
|
|
317
|
+
{ flag: '--staged', desc: 'Scan staged changes in Git index (default)' },
|
|
318
|
+
{ flag: '--placeholders', desc: 'Detect unfinished lazy placeholder stubs in newly added code' },
|
|
319
|
+
{ flag: '--scope <file>', desc: 'Verify staged files stay within declared task scope JSON' },
|
|
320
|
+
{ flag: '--enforce', desc: 'Fail with exit code 1 if violations are detected' },
|
|
321
|
+
{ flag: '--json', desc: 'Output scan results in versioned JSON format' },
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
hook: {
|
|
325
|
+
name: 'hook',
|
|
326
|
+
description: 'Manage safe pre-commit Git governance hooks',
|
|
327
|
+
usage: 'contextos hook <install|uninstall>',
|
|
328
|
+
requiresProject: false,
|
|
329
|
+
options: [
|
|
330
|
+
{ flag: 'install', desc: 'Install or update isolated pre-commit hook' },
|
|
331
|
+
{ flag: 'uninstall', desc: 'Remove ContextOS hook block while preserving custom hooks' },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
296
334
|
};
|
|
297
335
|
|
|
298
336
|
/**
|
package/bin/index.js
CHANGED
|
@@ -32,6 +32,15 @@ const flags = {
|
|
|
32
32
|
const i = args.indexOf('--add-skill');
|
|
33
33
|
return i !== -1 ? args[i + 1] : null;
|
|
34
34
|
})(),
|
|
35
|
+
project: (() => {
|
|
36
|
+
const i = args.indexOf('--project');
|
|
37
|
+
return i !== -1 && args[i + 1] && !args[i + 1].startsWith('-') ? path.resolve(args[i + 1]) : process.cwd();
|
|
38
|
+
})(),
|
|
39
|
+
target: (() => {
|
|
40
|
+
const i = args.indexOf('--target');
|
|
41
|
+
return i !== -1 && args[i + 1] && !args[i + 1].startsWith('-') ? args[i + 1] : null;
|
|
42
|
+
})(),
|
|
43
|
+
githubAnnotations: args.includes('--github-annotations'),
|
|
35
44
|
};
|
|
36
45
|
|
|
37
46
|
// ── Help / Version ────────────────────────────────────────────────────────────
|
|
@@ -64,12 +73,16 @@ Options:
|
|
|
64
73
|
--minimal Install only 7 core skills (lightweight footprint)
|
|
65
74
|
--profile <name> Install a specific profile (mvp, startup, enterprise, frontend, backend)
|
|
66
75
|
--auto Auto-detect tech stack and apply recommended profile
|
|
76
|
+
--project <path> Specify target project root directory (default: current directory)
|
|
77
|
+
--target <adapters> Target adapter(s) to verify or export (default: all)
|
|
78
|
+
--github-annotations Emit GitHub Actions workflow commands and step summary
|
|
67
79
|
--with-mcp, --mcp [DEPRECATED] Use the separate @contextos/mcp package instead
|
|
68
80
|
--skip-compile Skip running ctx.js export after installation
|
|
69
81
|
--add-skill <ref> Install a community plugin skill after setup
|
|
70
82
|
|
|
71
83
|
Commands:
|
|
72
84
|
init Install and configure .agents/ in target project
|
|
85
|
+
gate Run deterministic quality gate against adapter drift
|
|
73
86
|
status Display project configuration, active profile, and lockfile status
|
|
74
87
|
update Safely update skills without overwriting custom changes
|
|
75
88
|
uninstall Safely uninstall ContextOS files (preserves user custom skills)
|
|
@@ -84,6 +97,10 @@ Commands:
|
|
|
84
97
|
watch Start continuous file watcher and auto-sync daemon
|
|
85
98
|
detect Analyze project and display detected tech stack & IDE
|
|
86
99
|
install-skill Interactive skill installer (or pass <ref> / --from-repo)
|
|
100
|
+
scan Scan Git staged changes for secrets, placeholders, and scope
|
|
101
|
+
hook <cmd> Manage isolated pre-commit Git governance hooks (install, uninstall)
|
|
102
|
+
recover List or recover from interrupted filesystem transactions
|
|
103
|
+
explain [rule] Inspect rule catalog, enforcement levels, and checkers
|
|
87
104
|
setup-mcp [DEPRECATED] Add MCP execution server to an existing .agents/ project
|
|
88
105
|
|
|
89
106
|
Profiles:
|
|
@@ -250,20 +267,138 @@ if (mainCommand === 'recover') {
|
|
|
250
267
|
process.exit(0);
|
|
251
268
|
}
|
|
252
269
|
|
|
253
|
-
//
|
|
270
|
+
// Gate command (deterministic quality gate verification)
|
|
271
|
+
if (mainCommand === 'gate') {
|
|
272
|
+
const { runGate, emitGitHubAnnotations, writeGitHubSummary } = require('./lib/gate.js');
|
|
273
|
+
const target = flags.target || (args[1] && !args[1].startsWith('-') ? args[1] : 'all');
|
|
274
|
+
const result = runGate(flags.project, {
|
|
275
|
+
target,
|
|
276
|
+
profile: flags.profile,
|
|
277
|
+
json: flags.json,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (process.env.GITHUB_ACTIONS === 'true' || flags.githubAnnotations) {
|
|
281
|
+
emitGitHubAnnotations(result);
|
|
282
|
+
writeGitHubSummary(result);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (flags.json) {
|
|
286
|
+
console.log(JSON.stringify(result, null, 2));
|
|
287
|
+
} else {
|
|
288
|
+
console.log(`\nContextOS Quality Gate (${result.schemaVersion})\n`);
|
|
289
|
+
console.log(` Project: ${result.projectRoot}`);
|
|
290
|
+
console.log(` Status : ${result.status.toUpperCase()} (code ${result.code})`);
|
|
291
|
+
console.log(` Message: ${result.message}\n`);
|
|
292
|
+
if (result.drift && result.drift.hasDrift) {
|
|
293
|
+
console.log(`! Drift detected (${result.drift.totalFindings} findings):`);
|
|
294
|
+
for (const [state, items] of Object.entries(result.drift.findings)) {
|
|
295
|
+
if (items.length > 0) {
|
|
296
|
+
console.log(` [${state}] (${items.length}):`);
|
|
297
|
+
for (const item of items) {
|
|
298
|
+
console.log(` • ${item.path || item.reason || JSON.stringify(item)}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (result.drift.collisions && result.drift.collisions.length > 0) {
|
|
303
|
+
console.log(`\n [PATH_COLLISIONS] (${result.drift.collisions.length}):`);
|
|
304
|
+
for (const c of result.drift.collisions) {
|
|
305
|
+
console.log(` • ${c.path} (between ${c.firstAdapter} and ${c.secondAdapter})`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
process.exit(result.code);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Export check mode (in-process verification without spawning untrusted project scripts)
|
|
314
|
+
if (mainCommand === 'export' && args.includes('--check')) {
|
|
315
|
+
const { detectDrift } = require('../.agents/adapters/drift-detector.js');
|
|
316
|
+
const rawTarget = flags.target || (args[1] && !args[1].startsWith('-') ? args[1] : 'all');
|
|
317
|
+
const drift = detectDrift(flags.project, rawTarget, { profile: flags.profile });
|
|
318
|
+
|
|
319
|
+
if (process.env.GITHUB_ACTIONS === 'true' || flags.githubAnnotations) {
|
|
320
|
+
const { emitGitHubAnnotations, writeGitHubSummary } = require('./lib/gate.js');
|
|
321
|
+
const gateFormat = {
|
|
322
|
+
ok: !drift.hasDrift && !drift.hasError,
|
|
323
|
+
code: drift.code !== undefined ? drift.code : (drift.hasDrift ? 1 : 0),
|
|
324
|
+
status: drift.status || (drift.hasError ? 'error' : (drift.hasDrift ? 'drift' : 'pass')),
|
|
325
|
+
projectRoot: flags.project,
|
|
326
|
+
target: rawTarget,
|
|
327
|
+
profile: flags.profile || 'default',
|
|
328
|
+
drift,
|
|
329
|
+
message: drift.hasDrift ? 'Adapter drift detected' : 'Artifacts synchronized',
|
|
330
|
+
};
|
|
331
|
+
emitGitHubAnnotations(gateFormat);
|
|
332
|
+
writeGitHubSummary(gateFormat);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (flags.json) {
|
|
336
|
+
console.log(JSON.stringify(drift, null, 2));
|
|
337
|
+
} else {
|
|
338
|
+
console.log('\nContextOS - Adapter Output Drift Check\n');
|
|
339
|
+
console.log(` Project: ${flags.project}`);
|
|
340
|
+
if (!drift.hasDrift && !drift.hasError) {
|
|
341
|
+
console.log(`✓ No adapter drift detected. All ${drift.projectedCount} output artifacts are synchronized.`);
|
|
342
|
+
} else {
|
|
343
|
+
console.log(`! Drift detected (${drift.totalFindings} finding(s)):\n`);
|
|
344
|
+
for (const [state, items] of Object.entries(drift.findings)) {
|
|
345
|
+
if (items.length > 0) {
|
|
346
|
+
console.log(` [${state}] (${items.length}):`);
|
|
347
|
+
for (const item of items) {
|
|
348
|
+
console.log(` • ${item.path || item.reason || JSON.stringify(item)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (drift.collisions && drift.collisions.length > 0) {
|
|
353
|
+
console.log(`\n [PATH_COLLISIONS] (${drift.collisions.length}):`);
|
|
354
|
+
for (const c of drift.collisions) {
|
|
355
|
+
console.log(` • ${c.path} (between ${c.firstAdapter} and ${c.secondAdapter})`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
console.log('\nRun: contextos export all to synchronize outputs with source skills.\n');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
process.exit(drift.code !== undefined ? drift.code : (drift.hasDrift ? 1 : 0));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Scan command (Git staged index security and placeholder scanner)
|
|
365
|
+
if (mainCommand === 'scan') {
|
|
366
|
+
const scanModule = require('./commands/scan.js');
|
|
367
|
+
scanModule(args, flags);
|
|
368
|
+
process.exit(0);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Hook command (safe Git hook lifecycle management)
|
|
372
|
+
if (mainCommand === 'hook') {
|
|
373
|
+
const { hookCommand } = require('./commands/hook.js');
|
|
374
|
+
hookCommand(args, flags);
|
|
375
|
+
process.exit(0);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Proxy commands to trusted package .agents/ctx.js targeting flags.project
|
|
254
379
|
const PROXY_COMMANDS = [
|
|
255
380
|
'profile', 'export', 'validate', 'resolve', 'skill', 'index',
|
|
256
381
|
'clean-worktrees', 'stats', 'watch', 'compile', 'explain',
|
|
257
382
|
'thread'
|
|
258
383
|
];
|
|
259
384
|
|
|
260
|
-
const
|
|
261
|
-
const hasLocalCtx = fs.existsSync(ctxPath);
|
|
385
|
+
const packageCtxPath = path.join(__dirname, '..', '.agents', 'ctx.js');
|
|
262
386
|
|
|
263
|
-
if (mainCommand && PROXY_COMMANDS.includes(mainCommand)
|
|
387
|
+
if (mainCommand && PROXY_COMMANDS.includes(mainCommand)) {
|
|
264
388
|
const { execFileSync } = require('child_process');
|
|
389
|
+
const filteredArgs = [];
|
|
390
|
+
for (let i = 0; i < args.length; i++) {
|
|
391
|
+
if (args[i] === '--project') {
|
|
392
|
+
i++; // skip project path
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
filteredArgs.push(args[i]);
|
|
396
|
+
}
|
|
265
397
|
try {
|
|
266
|
-
execFileSync(process.execPath, [
|
|
398
|
+
execFileSync(process.execPath, [packageCtxPath, ...filteredArgs], {
|
|
399
|
+
cwd: flags.project,
|
|
400
|
+
stdio: 'inherit',
|
|
401
|
+
});
|
|
267
402
|
} catch (e) {
|
|
268
403
|
process.exit(e.status || 1);
|
|
269
404
|
}
|
|
@@ -300,34 +435,7 @@ if (mainCommand === 'watch') {
|
|
|
300
435
|
watchModule.runWatch(process.cwd());
|
|
301
436
|
}
|
|
302
437
|
|
|
303
|
-
const PROJECT_ONLY_COMMANDS = ['profile', 'export', 'validate', 'resolve', 'skill', 'index', 'clean-worktrees', 'compile', 'explain'];
|
|
304
|
-
if (mainCommand && PROJECT_ONLY_COMMANDS.includes(mainCommand) && !hasLocalCtx) {
|
|
305
|
-
console.error('[ERROR] .agents/ctx.js not found in current directory.');
|
|
306
|
-
console.error(' Are you in a ContextOS project? Run `contextos` or `npx contextos-agents` first.');
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (mainCommand === 'audit') {
|
|
311
|
-
if (!hasLocalCtx) {
|
|
312
|
-
console.error('[ERROR] .agents/ctx.js not found. Are you in a ContextOS project?');
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
const { execFileSync } = require('child_process');
|
|
316
|
-
try {
|
|
317
|
-
execFileSync(process.execPath, [ctxPath, 'validate'], { stdio: 'inherit' });
|
|
318
|
-
} catch (e) {
|
|
319
|
-
process.exit(1);
|
|
320
|
-
}
|
|
321
|
-
process.exit(0);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
438
|
if (mainCommand === 'install-skill') {
|
|
325
|
-
const ctxPath = path.join(process.cwd(), '.agents', 'ctx.js');
|
|
326
|
-
if (!fs.existsSync(ctxPath)) {
|
|
327
|
-
console.error('[ERROR] .agents/ctx.js not found. Are you in a ContextOS project?');
|
|
328
|
-
process.exit(1);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
439
|
const fromRepo = (() => {
|
|
332
440
|
const i = args.indexOf('--from-repo');
|
|
333
441
|
return i !== -1 ? args[i + 1] : null;
|
|
@@ -338,7 +446,10 @@ if (mainCommand === 'install-skill') {
|
|
|
338
446
|
if (ref) {
|
|
339
447
|
const { execFileSync } = require('child_process');
|
|
340
448
|
try {
|
|
341
|
-
execFileSync(process.execPath, [
|
|
449
|
+
execFileSync(process.execPath, [packageCtxPath, 'skill', 'add', ref], {
|
|
450
|
+
cwd: flags.project,
|
|
451
|
+
stdio: 'inherit',
|
|
452
|
+
});
|
|
342
453
|
} catch (e) {
|
|
343
454
|
process.exit(1);
|
|
344
455
|
}
|