@minhpnq1807/contextos 0.5.37 → 0.5.39
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/CHANGELOG.md +18 -0
- package/bin/ctx.js +59 -60
- package/package.json +1 -1
- package/plugins/ctx/lib/reporter.js +87 -65
- package/plugins/ctx/lib/scheduler.js +15 -13
- package/plugins/ctx/lib/stop-hook.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.39
|
|
4
|
+
|
|
5
|
+
- **Report layout fix:** Replaced ASCII table formatting with clean markdown output. Reports now render correctly in all agent UIs (Antigravity, Claude Code, Codex) without truncation or line-wrapping issues.
|
|
6
|
+
- **Skills & workflows in report:** `Suggested Skills` and `Suggested Workflows` sections now appear in the compliance report when available. Data is passed through from `stop-hook.js` → `buildReport()` → `formatReport()`.
|
|
7
|
+
- **No more truncation:** All rules now display in the report — removed the artificial item limits that caused `... N more` messages.
|
|
8
|
+
- **Emoji status indicators:** Rule outcomes use ✅/❌/❓/⚠️ prefixes for quick scanning.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## 0.5.38
|
|
12
|
+
|
|
13
|
+
- **Unified agent branding:** All user-facing text now shows `antigravity` instead of `agy`. Internal value remains `agy` for backward compatibility. Interactive prompts display "Antigravity" without the parenthetical alias.
|
|
14
|
+
- **`ctx help` command:** Added `help` as a recognized command (alongside `--help` and `-h`). Previously returned "Unknown command".
|
|
15
|
+
- **`--help` in usage:** Added `ctx --help` line to the usage display so it's discoverable.
|
|
16
|
+
- **Fix `--agents` flag parsing:** `ctx install --agents antigravity` now correctly skips the interactive prompt. Previously only `--agent` (singular) was recognized.
|
|
17
|
+
- **Cleaner usage text:** Replaced hardcoded agent lists (`codex,claude,antigravity,copilot`) with `<names>` placeholder for future-proof documentation.
|
|
18
|
+
|
|
3
19
|
## 0.5.37
|
|
4
20
|
|
|
5
21
|
- **Real-time animated progress bar for `ctx install`:** The progress spinner now updates in-place using raw stderr writes (`\r`) instead of being captured by `streamSetupOutput`. Uses a smooth 10-frame Braille spinner (`⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`) with a visual bar (`[████████░░░░]`) that animates at 80ms intervals.
|
|
6
22
|
- **Clean install output:** Reduced verbose per-agent install summary from 10+ lines of paths to a compact 4-line summary (Hooks →, MCP →, Embeddings count, restart instruction). Removed redundant "embedding model already cached" log line from `warmInstallEmbeddings`.
|
|
7
23
|
- **Fix `streamSetupOutput` breaking spinner:** Previously intercepted `process.stderr.write` and converted `\r` carriage returns to newlines, preventing in-place updates. Now only intercepts `console.log` for `│ ` prefixed output, leaving stderr untouched for spinner rendering.
|
|
24
|
+
- **Suppress codex command noise:** `runCodex()` no longer prints verbose stdout lines like "Added marketplace `contextos`" and "Added global MCP server 'ctx-mcp'" — the progress spinner already provides feedback.
|
|
25
|
+
- **Clean context formatting:** Removed absolute paths from rules, skills, and workflows in scheduler output. Removed duplicate "ContextOS reminders" section. Truncated long skill descriptions to 80 chars. Capped rules at 5 per section instead of 8. Context now renders as clean, readable markdown:
|
|
8
26
|
|
|
9
27
|
## 0.5.36
|
|
10
28
|
|
package/bin/ctx.js
CHANGED
|
@@ -43,44 +43,45 @@ function usage() {
|
|
|
43
43
|
return `ContextOS (ctx)
|
|
44
44
|
|
|
45
45
|
Usage:
|
|
46
|
-
ctx install
|
|
47
|
-
ctx install --agent <name>
|
|
48
|
-
ctx install --copy
|
|
49
|
-
ctx setup
|
|
50
|
-
ctx setup --yes
|
|
51
|
-
ctx setup --agents
|
|
52
|
-
ctx setup --no-rules
|
|
53
|
-
ctx setup --no-skills
|
|
54
|
-
ctx setup --quiet
|
|
55
|
-
ctx debug -- "task"
|
|
56
|
-
ctx report
|
|
57
|
-
ctx evidence
|
|
58
|
-
ctx stats
|
|
59
|
-
ctx benchmark -- "task"
|
|
60
|
-
ctx sync --rules
|
|
61
|
-
ctx sync --rules --agents
|
|
62
|
-
ctx sync --rules --dry-run
|
|
63
|
-
ctx sync --rules --no-import-codex-mcp
|
|
64
|
-
ctx sync --skills
|
|
65
|
-
ctx sync --skills --agents
|
|
66
|
-
ctx sync --skills --dry-run
|
|
67
|
-
ctx sync --skills --no-collect
|
|
68
|
-
ctx sync --skills --no-embeddings
|
|
69
|
-
ctx sync --skills --verbose
|
|
70
|
-
ctx sync --workflows
|
|
71
|
-
ctx sync --workflows --agents
|
|
72
|
-
ctx sync --workflows --dry-run
|
|
73
|
-
ctx embeddings warm -- "task"
|
|
74
|
-
ctx ruler -- <ruler args>
|
|
75
|
-
ctx skillshare -- <skillshare args>
|
|
76
|
-
ctx --
|
|
46
|
+
ctx install Interactive multi-select agent installer
|
|
47
|
+
ctx install --agent <name> Install a specific agent (codex|claude|antigravity|copilot)
|
|
48
|
+
ctx install --copy Legacy: copy plugin folder only (no hooks/mcp)
|
|
49
|
+
ctx setup Interactive full setup wizard
|
|
50
|
+
ctx setup --yes Auto-confirm all setup prompts
|
|
51
|
+
ctx setup --agents <names> Pre-select agents to install
|
|
52
|
+
ctx setup --no-rules Skip AGENTS.md rule sync
|
|
53
|
+
ctx setup --no-skills Skip skill sync
|
|
54
|
+
ctx setup --quiet Quiet mode (minimal output)
|
|
55
|
+
ctx debug -- "task" Debug a task with ContextOS tracing
|
|
56
|
+
ctx report Show last ContextOS compliance report
|
|
57
|
+
ctx evidence Show evidence from last report
|
|
58
|
+
ctx stats Show workspace statistics
|
|
59
|
+
ctx benchmark -- "task" Benchmark workspace for a task
|
|
60
|
+
ctx sync --rules Sync AGENTS.md rules to all agents
|
|
61
|
+
ctx sync --rules --agents <names> Sync rules to specific agents only
|
|
62
|
+
ctx sync --rules --dry-run Preview rule sync without writing
|
|
63
|
+
ctx sync --rules --no-import-codex-mcp Skip importing Codex MCP servers
|
|
64
|
+
ctx sync --skills Sync skills across agents
|
|
65
|
+
ctx sync --skills --agents <names> Sync skills to specific agents only
|
|
66
|
+
ctx sync --skills --dry-run Preview skill sync without writing
|
|
67
|
+
ctx sync --skills --no-collect Skip collecting new skills
|
|
68
|
+
ctx sync --skills --no-embeddings Skip embedding generation
|
|
69
|
+
ctx sync --skills --verbose Verbose skill sync output
|
|
70
|
+
ctx sync --workflows Sync workflows across agents
|
|
71
|
+
ctx sync --workflows --agents <names> Sync workflows to specific agents
|
|
72
|
+
ctx sync --workflows --dry-run Preview workflow sync without writing
|
|
73
|
+
ctx embeddings warm -- "task" Pre-warm embedding caches for a task
|
|
74
|
+
ctx ruler -- <ruler args> Passthrough to ruler CLI
|
|
75
|
+
ctx skillshare -- <skillshare args> Passthrough to skillshare CLI
|
|
76
|
+
ctx --help Show this help message
|
|
77
|
+
ctx --version Show installed version
|
|
77
78
|
`;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
const SUPPORTED_AGENTS = [
|
|
81
82
|
{ label: "Codex", value: "codex", selected: false },
|
|
82
83
|
{ label: "Claude Code", value: "claude", selected: false },
|
|
83
|
-
{ label: "Antigravity
|
|
84
|
+
{ label: "Antigravity", value: "agy", selected: false },
|
|
84
85
|
{ label: "GitHub Copilot", value: "copilot", selected: false }
|
|
85
86
|
];
|
|
86
87
|
|
|
@@ -92,10 +93,10 @@ function normalizeInstallAgent(agent) {
|
|
|
92
93
|
"Install one agent per command:",
|
|
93
94
|
" ctx install --agent codex",
|
|
94
95
|
" ctx install --agent claude",
|
|
95
|
-
" ctx install --agent
|
|
96
|
+
" ctx install --agent antigravity",
|
|
96
97
|
" ctx install --agent copilot",
|
|
97
98
|
"",
|
|
98
|
-
"Do not run `ctx install --agent codex|claude|
|
|
99
|
+
"Do not run `ctx install --agent codex|claude|antigravity|copilot`: `|` is a shell pipe."
|
|
99
100
|
].join("\n"));
|
|
100
101
|
}
|
|
101
102
|
if (normalized === "antigravity") return "agy";
|
|
@@ -369,23 +370,14 @@ function tryRunCodex(args) {
|
|
|
369
370
|
|
|
370
371
|
function runCodex(args) {
|
|
371
372
|
try {
|
|
372
|
-
|
|
373
|
+
execFileSync("codex", args, {
|
|
373
374
|
stdio: ["ignore", "pipe", "pipe"],
|
|
374
375
|
encoding: "utf8",
|
|
375
376
|
shell: true
|
|
376
377
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
console.log(line);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
378
|
+
// Suppress stdout (e.g. "Added marketplace…", "Added global MCP server…")
|
|
379
|
+
// — the progress spinner already provides feedback.
|
|
382
380
|
} catch (error) {
|
|
383
|
-
// Log any output captured before the error
|
|
384
|
-
if (error.stdout && error.stdout.trim()) {
|
|
385
|
-
for (const line of error.stdout.trim().split(/\r?\n/)) {
|
|
386
|
-
console.log(line);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
381
|
const status = typeof error.status === "number" ? error.status : 1;
|
|
390
382
|
throw new Error(`codex ${args.join(" ")} failed with exit code ${status}. Make sure Codex CLI is installed and authenticated.`);
|
|
391
383
|
}
|
|
@@ -551,7 +543,7 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
551
543
|
options: [
|
|
552
544
|
{ label: "Codex", value: "codex", selected: options.agents.includes("codex") },
|
|
553
545
|
{ label: "Claude", value: "claude", selected: options.agents.includes("claude") },
|
|
554
|
-
{ label: "Antigravity
|
|
546
|
+
{ label: "Antigravity", value: "agy", selected: options.agents.includes("agy") },
|
|
555
547
|
{ label: "GitHub Copilot", value: "copilot", selected: options.agents.includes("copilot") }
|
|
556
548
|
]
|
|
557
549
|
});
|
|
@@ -579,7 +571,7 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
579
571
|
for (const line of setupSummaryLines({ cwd, ...options })) console.log(`│ ${line}`);
|
|
580
572
|
console.log("");
|
|
581
573
|
|
|
582
|
-
if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,
|
|
574
|
+
if (!options.agents.length) throw new Error("No agents selected. Use --agents codex,claude,antigravity,copilot.");
|
|
583
575
|
|
|
584
576
|
for (const agent of options.agents) {
|
|
585
577
|
console.log(`◇ Setting up ${agent}...`);
|
|
@@ -621,26 +613,33 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
621
613
|
const args = process.argv.slice(2);
|
|
622
614
|
const command = args[0];
|
|
623
615
|
|
|
624
|
-
function
|
|
625
|
-
const agentFlag = args.indexOf("--agent");
|
|
626
|
-
if (agentFlag >= 0)
|
|
627
|
-
|
|
616
|
+
function installAgentsFromArgs(args) {
|
|
617
|
+
const agentFlag = Math.max(args.indexOf("--agent"), args.indexOf("--agents"));
|
|
618
|
+
if (agentFlag >= 0) {
|
|
619
|
+
const value = args[agentFlag + 1] || "";
|
|
620
|
+
return parseAgentList(value).map(normalizeInstallAgent).filter(Boolean);
|
|
621
|
+
}
|
|
622
|
+
return null; // no flag → interactive selection
|
|
628
623
|
}
|
|
629
624
|
|
|
630
625
|
try {
|
|
631
|
-
if (!command || command === "--help" || command === "-h") {
|
|
626
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
632
627
|
console.log(usage());
|
|
633
628
|
} else if (command === "--version" || command === "-v") {
|
|
634
629
|
console.log(packageVersion());
|
|
635
630
|
} else if (command === "install") {
|
|
636
631
|
const copy = args.includes("--copy");
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
// Direct mode: ctx install --
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
632
|
+
const explicitAgents = installAgentsFromArgs(args);
|
|
633
|
+
|
|
634
|
+
if (explicitAgents && explicitAgents.length) {
|
|
635
|
+
// Direct mode: ctx install --agents antigravity,codex
|
|
636
|
+
for (const agent of explicitAgents) {
|
|
637
|
+
console.log(`◇ Installing ${agent}...`);
|
|
638
|
+
await streamSetupOutput(() => install({ copy, agent }));
|
|
639
|
+
console.log("");
|
|
640
|
+
}
|
|
641
|
+
} else if (explicitAgents && !explicitAgents.length) {
|
|
642
|
+
console.log("No valid agents specified. Use --agents codex,claude,antigravity,copilot.");
|
|
644
643
|
} else {
|
|
645
644
|
// Interactive mode: ctx install
|
|
646
645
|
const selected = await multiSelect({
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isSystemUserRule } from "./analyzer.js";
|
|
2
|
-
import { section, table, truncateCell } from "./terminal-ui.js";
|
|
3
2
|
|
|
4
|
-
export function buildReport({ cwd, prompt, relevantFiles, scheduled, gitSnapshot, compliance, runtimeEvidence }) {
|
|
3
|
+
export function buildReport({ cwd, prompt, relevantFiles, suggestedSkills, suggestedWorkflows, scheduled, gitSnapshot, compliance, runtimeEvidence }) {
|
|
5
4
|
const actionableCompliance = compliance.filter((item) => !isSystemUserRule(item.rule));
|
|
6
5
|
const followed = actionableCompliance.filter((item) => item.status === "followed");
|
|
7
6
|
const ignored = actionableCompliance.filter((item) => item.status === "ignored");
|
|
@@ -16,6 +15,8 @@ export function buildReport({ cwd, prompt, relevantFiles, scheduled, gitSnapshot
|
|
|
16
15
|
prompt,
|
|
17
16
|
injectedRuleCount: (scheduled?.highRules?.length || 0) + (scheduled?.midRules?.length || 0),
|
|
18
17
|
relevantFiles,
|
|
18
|
+
suggestedSkills: suggestedSkills || [],
|
|
19
|
+
suggestedWorkflows: suggestedWorkflows || [],
|
|
19
20
|
changedFiles: gitSnapshot.changedFiles,
|
|
20
21
|
warnings: gitSnapshot.warnings || [],
|
|
21
22
|
runtimeEvidence: summarizeRuntimeEvidence(runtimeEvidence),
|
|
@@ -33,47 +34,77 @@ export function buildReport({ cwd, prompt, relevantFiles, scheduled, gitSnapshot
|
|
|
33
34
|
export function formatReport(report) {
|
|
34
35
|
report = sanitizeReport(report);
|
|
35
36
|
const lines = [];
|
|
36
|
-
lines.push("ContextOS
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
lines.push("# ContextOS Report\n");
|
|
38
|
+
|
|
39
|
+
// Summary
|
|
40
|
+
lines.push("## Summary");
|
|
41
|
+
lines.push(`- **Efficiency:** ${report.efficiencyScore == null ? "unknown" : `${report.efficiencyScore}%`}`);
|
|
42
|
+
lines.push(`- **Injected rules:** ${report.injectedRuleCount || 0}`);
|
|
43
|
+
lines.push(`- **Measured rules:** ${report.measuredRuleCount ?? ((report.followed?.length || 0) + (report.ignored?.length || 0))}`);
|
|
44
|
+
lines.push(`- **Changed files:** ${report.changedFiles?.length ? report.changedFiles.length : "none detected"}`);
|
|
45
|
+
lines.push("");
|
|
46
|
+
|
|
47
|
+
// Rule Outcomes
|
|
48
|
+
lines.push("## Rule Outcomes");
|
|
49
|
+
lines.push(`- ✅ Followed: ${report.followed?.length || 0}`);
|
|
50
|
+
lines.push(`- ❌ Ignored: ${report.ignored?.length || 0}`);
|
|
51
|
+
lines.push(`- ❓ Unknown: ${report.unknown?.length || 0}`);
|
|
52
|
+
lines.push(`- ⚠️ Unmeasurable: ${report.unmeasurable?.length || 0}`);
|
|
53
|
+
lines.push("");
|
|
54
|
+
|
|
55
|
+
// Suggested Files
|
|
53
56
|
if (report.relevantFiles?.length) {
|
|
54
|
-
lines.push(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
lines.push("## Suggested Files");
|
|
58
|
+
for (const [index, file] of report.relevantFiles.entries()) {
|
|
59
|
+
const score = typeof file.score === "number" ? ` (${file.score.toFixed(2)})` : "";
|
|
60
|
+
lines.push(`${index + 1}. ${file.path}${score}`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Suggested Skills
|
|
66
|
+
if (report.suggestedSkills?.length) {
|
|
67
|
+
lines.push("## Suggested Skills");
|
|
68
|
+
for (const skill of report.suggestedSkills) {
|
|
69
|
+
const desc = skill.description ? `: ${truncate(skill.description, 80)}` : "";
|
|
70
|
+
lines.push(`- **${skill.name}**${desc}`);
|
|
71
|
+
}
|
|
72
|
+
lines.push("");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Suggested Workflows
|
|
76
|
+
if (report.suggestedWorkflows?.length) {
|
|
77
|
+
lines.push("## Suggested Workflows");
|
|
78
|
+
for (const workflow of report.suggestedWorkflows) {
|
|
79
|
+
const name = workflow.title || workflow.name;
|
|
80
|
+
const chain = workflow.chain?.length ? ` → ${workflow.chain.join(" → ")}` : "";
|
|
81
|
+
lines.push(`- **${name}**${chain}`);
|
|
82
|
+
}
|
|
83
|
+
lines.push("");
|
|
60
84
|
}
|
|
85
|
+
|
|
86
|
+
// Runtime Telemetry
|
|
61
87
|
if (report.runtimeEvidence?.signals?.length) {
|
|
62
|
-
lines.push(
|
|
63
|
-
|
|
88
|
+
lines.push("## Runtime Telemetry");
|
|
89
|
+
for (const signal of report.runtimeEvidence.signals) {
|
|
90
|
+
lines.push(`- ${signal}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push("");
|
|
64
93
|
}
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
// Warnings
|
|
96
|
+
for (const warning of report.warnings || []) lines.push(`> ⚠️ ${warning}\n`);
|
|
67
97
|
|
|
98
|
+
// Rule details
|
|
68
99
|
appendBucket(lines, "Followed", report.followed);
|
|
69
100
|
appendBucket(lines, "Ignored", report.ignored);
|
|
70
101
|
appendBucket(lines, "Unknown", report.unknown);
|
|
71
102
|
appendBucket(lines, "Unmeasurable", report.unmeasurable);
|
|
72
103
|
|
|
73
104
|
if (report.ignored?.length) {
|
|
74
|
-
lines.push(
|
|
105
|
+
lines.push(`> **Suggestion:** Fix ignored rule evidence first: ${truncate(report.ignored[0].rule?.content || "", 70)}`);
|
|
75
106
|
} else if (report.unknown?.length && !(report.followed?.length || report.ignored?.length)) {
|
|
76
|
-
lines.push("Suggestion
|
|
107
|
+
lines.push("> **Suggestion:** These rules need runtime evidence or more concrete keywords before ContextOS can score them from git diff.");
|
|
77
108
|
}
|
|
78
109
|
|
|
79
110
|
return lines.join("\n");
|
|
@@ -82,15 +113,15 @@ export function formatReport(report) {
|
|
|
82
113
|
export function formatEvidence(report) {
|
|
83
114
|
report = sanitizeReport(report);
|
|
84
115
|
const lines = [];
|
|
85
|
-
lines.push("ContextOS
|
|
86
|
-
|
|
87
|
-
lines.push(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
lines.push("# ContextOS Evidence\n");
|
|
117
|
+
|
|
118
|
+
lines.push("## Summary");
|
|
119
|
+
lines.push(`- **Prompt:** ${truncate(report.prompt || "(empty)", 100)}`);
|
|
120
|
+
lines.push(`- **Efficiency:** ${report.efficiencyScore == null ? "unknown" : `${report.efficiencyScore}%`}`);
|
|
121
|
+
lines.push(`- **Changed files:** ${report.changedFiles?.length ? report.changedFiles.join(", ") : "none detected"}`);
|
|
122
|
+
lines.push("");
|
|
92
123
|
|
|
93
|
-
for (const warning of report.warnings || []) lines.push(
|
|
124
|
+
for (const warning of report.warnings || []) lines.push(`> ⚠️ ${warning}\n`);
|
|
94
125
|
|
|
95
126
|
const items = [
|
|
96
127
|
...(report.followed || []).map((item) => ({ ...item, status: "followed" })),
|
|
@@ -105,43 +136,34 @@ export function formatEvidence(report) {
|
|
|
105
136
|
return lines.join("\n");
|
|
106
137
|
}
|
|
107
138
|
|
|
108
|
-
lines.push(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
item.status.toUpperCase()
|
|
112
|
-
|
|
113
|
-
item.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
items.forEach((item, index) => {
|
|
119
|
-
lines.push(section(`${index + 1}. ${item.status.toUpperCase()}`));
|
|
120
|
-
lines.push(table(["Field", "Value"], [
|
|
121
|
-
["Rule", truncateCell(item.rule?.content || "(missing rule)", 120)],
|
|
122
|
-
["Source", item.rule?.sourcePath || ""],
|
|
123
|
-
["Score", typeof item.rule?.score === "number" ? item.rule.score.toFixed(2) : ""],
|
|
124
|
-
["Kind", item.kind || ""],
|
|
125
|
-
["Keywords", item.keywords?.length ? truncateCell(item.keywords.join(", "), 120) : ""],
|
|
126
|
-
["Evidence", truncateCell(item.evidence || "(none)", 120)]
|
|
127
|
-
].filter(([, value]) => value !== "")));
|
|
139
|
+
lines.push("## Evidence Details\n");
|
|
140
|
+
for (const [index, item] of items.entries()) {
|
|
141
|
+
const statusIcon = { followed: "✅", ignored: "❌", unknown: "❓", unmeasurable: "⚠️" }[item.status] || "";
|
|
142
|
+
lines.push(`### ${index + 1}. ${statusIcon} ${item.status.toUpperCase()}`);
|
|
143
|
+
lines.push(`- **Rule:** ${truncate(item.rule?.content || "(missing rule)", 120)}`);
|
|
144
|
+
if (item.rule?.sourcePath) lines.push(`- **Source:** ${item.rule.sourcePath}`);
|
|
145
|
+
if (typeof item.rule?.score === "number") lines.push(`- **Score:** ${item.rule.score.toFixed(2)}`);
|
|
146
|
+
if (item.kind) lines.push(`- **Kind:** ${item.kind}`);
|
|
147
|
+
lines.push(`- **Evidence:** ${truncate(item.evidence || "(none)", 120)}`);
|
|
128
148
|
for (const line of item.matchedLines || []) {
|
|
129
149
|
const where = line.file ? `${line.file}${typeof line.line === "number" ? `:${line.line}` : ""}` : "diff";
|
|
130
|
-
lines.push(`
|
|
150
|
+
lines.push(` - Match: \`${where}\` ${truncate(line.content || "", 100)}`);
|
|
131
151
|
}
|
|
132
|
-
|
|
152
|
+
lines.push("");
|
|
153
|
+
}
|
|
133
154
|
|
|
134
155
|
return lines.join("\n");
|
|
135
156
|
}
|
|
136
157
|
|
|
137
158
|
function appendBucket(lines, label, items = []) {
|
|
138
159
|
if (!items.length) return;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
lines.push(
|
|
160
|
+
const icon = { Followed: "✅", Ignored: "❌", Unknown: "❓", Unmeasurable: "⚠️" }[label] || "";
|
|
161
|
+
lines.push(`### ${icon} ${label}`);
|
|
162
|
+
for (const item of items) {
|
|
163
|
+
lines.push(`- **Rule:** ${truncate(item.rule.content, 100)}`);
|
|
164
|
+
lines.push(` - Evidence: ${truncate(item.evidence, 100)}`);
|
|
143
165
|
}
|
|
144
|
-
|
|
166
|
+
lines.push("");
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
function truncate(value, max) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
const MAX_CONTEXT_CHARS = 4000;
|
|
2
3
|
|
|
3
4
|
export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkills = [], suggestedWorkflows = [], maxChars = MAX_CONTEXT_CHARS } = {}) {
|
|
@@ -8,7 +9,7 @@ export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkill
|
|
|
8
9
|
|
|
9
10
|
const sections = [];
|
|
10
11
|
if (high.length) {
|
|
11
|
-
sections.push(section("Critical ContextOS rules", high.slice(0,
|
|
12
|
+
sections.push(section("Critical ContextOS rules", high.slice(0, 5).map(formatRule)));
|
|
12
13
|
}
|
|
13
14
|
if (relevantFiles.length) {
|
|
14
15
|
sections.push(section("Suggested files to check", relevantFiles.map((file) => `- ${file.path}`)));
|
|
@@ -20,10 +21,7 @@ export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkill
|
|
|
20
21
|
sections.push(section("Suggested workflow for this task", suggestedWorkflows.map(formatWorkflow)));
|
|
21
22
|
}
|
|
22
23
|
if (mid.length) {
|
|
23
|
-
sections.push(section("Additional relevant rules", mid.slice(0,
|
|
24
|
-
}
|
|
25
|
-
if (high.length) {
|
|
26
|
-
sections.push(section("ContextOS reminders", high.slice(0, 5).map(formatRule)));
|
|
24
|
+
sections.push(section("Additional relevant rules", mid.slice(0, 5).map(formatRule)));
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
const additionalContext = trimToLimit(sections.filter(Boolean).join("\n\n"), maxChars);
|
|
@@ -57,24 +55,28 @@ function section(title, lines) {
|
|
|
57
55
|
return `## ${title}\n${lines.join("\n")}`;
|
|
58
56
|
}
|
|
59
57
|
|
|
58
|
+
|
|
60
59
|
function formatRule(rule) {
|
|
61
|
-
|
|
62
|
-
return `- ${rule.content}${source}`;
|
|
60
|
+
return `- ${rule.content}`;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
function formatSkill(skill) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
const desc = skill.description
|
|
65
|
+
? `: ${truncate(skill.description, 80)}`
|
|
66
|
+
: "";
|
|
67
|
+
return `- ${skill.name}${desc}`;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
function formatWorkflow(workflow) {
|
|
72
71
|
const name = workflow.title || workflow.name;
|
|
73
72
|
const hint = workflow.hint ? `: ${workflow.hint}` : "";
|
|
74
73
|
const chain = workflow.chain?.length ? `\n chain: ${workflow.chain.join(" -> ")}` : "";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
return `- ${name}${hint}${chain}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function truncate(text, maxLen) {
|
|
78
|
+
if (text.length <= maxLen) return text;
|
|
79
|
+
return `${text.slice(0, maxLen - 1)}…`;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
function trimToLimit(value, maxChars) {
|
|
@@ -42,6 +42,8 @@ export function handleStopPayload(payload, { contextPath, reportPath, historyPat
|
|
|
42
42
|
cwd,
|
|
43
43
|
prompt: promptContext?.prompt || "",
|
|
44
44
|
relevantFiles: promptContext?.relevantFiles || [],
|
|
45
|
+
suggestedSkills: promptContext?.suggestedSkills || [],
|
|
46
|
+
suggestedWorkflows: promptContext?.suggestedWorkflows || [],
|
|
45
47
|
scheduled,
|
|
46
48
|
gitSnapshot,
|
|
47
49
|
compliance,
|