@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 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 Interactive multi-select agent installer
47
- ctx install --agent <name> Install a specific agent (codex|claude|agy|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 codex,claude,agy,copilot 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 codex,claude,agy,copilot 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 codex,claude,agy,copilot 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 codex,claude,agy,copilot 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 --version Show installed version
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 (agy)", value: "agy", selected: false },
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 agy",
96
+ " ctx install --agent antigravity",
96
97
  " ctx install --agent copilot",
97
98
  "",
98
- "Do not run `ctx install --agent codex|claude|agy|copilot`: `|` is a shell pipe."
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
- const stdout = execFileSync("codex", args, {
373
+ execFileSync("codex", args, {
373
374
  stdio: ["ignore", "pipe", "pipe"],
374
375
  encoding: "utf8",
375
376
  shell: true
376
377
  });
377
- if (stdout && stdout.trim()) {
378
- for (const line of stdout.trim().split(/\r?\n/)) {
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 (agy)", value: "agy", selected: options.agents.includes("agy") },
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,agy,copilot.");
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 installAgentFromArgs(args) {
625
- const agentFlag = args.indexOf("--agent");
626
- if (agentFlag >= 0) return normalizeInstallAgent(args[agentFlag + 1] || "");
627
- return null; // no --agent flag interactive selection
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 explicitAgent = installAgentFromArgs(args);
638
-
639
- if (explicitAgent) {
640
- // Direct mode: ctx install --agent <name>
641
- console.log(`◇ Installing ${explicitAgent}...`);
642
- await streamSetupOutput(() => install({ copy, agent: explicitAgent }));
643
- console.log("");
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.37",
3
+ "version": "0.5.39",
4
4
  "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex, Claude Code, and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 report");
37
- lines.push(section("Summary"));
38
- lines.push(table(["Metric", "Value"], [
39
- ["Efficiency", report.efficiencyScore == null ? "unknown" : `${report.efficiencyScore}%`],
40
- ["Injected rules", report.injectedRuleCount || 0],
41
- ["Measured rules", report.measuredRuleCount ?? ((report.followed?.length || 0) + (report.ignored?.length || 0))],
42
- ["Changed files", report.changedFiles?.length ? report.changedFiles.length : "none detected"]
43
- ]));
44
-
45
- lines.push(section("Rule Outcomes"));
46
- lines.push(table(["Status", "Count"], [
47
- ["followed", report.followed?.length || 0],
48
- ["ignored", report.ignored?.length || 0],
49
- ["unknown", report.unknown?.length || 0],
50
- ["unmeasurable", report.unmeasurable?.length || 0]
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(section("Suggested Files"));
55
- lines.push(table(["#", "Path", "Score"], report.relevantFiles.slice(0, 10).map((file, index) => [
56
- index + 1,
57
- truncateCell(file.path, 90),
58
- typeof file.score === "number" ? file.score.toFixed(2) : ""
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(section("Runtime Telemetry"));
63
- lines.push(table(["#", "Signal"], report.runtimeEvidence.signals.map((signal, index) => [index + 1, signal])));
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
- for (const warning of report.warnings || []) lines.push(`Warning: ${warning}`);
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(`Suggestion: fix ignored rule evidence first: ${truncate(report.ignored[0].rule?.content || "", 70)}`);
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: these rules need runtime evidence or more concrete keywords before ContextOS can score them from git diff.");
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 evidence");
86
- lines.push(section("Summary"));
87
- lines.push(table(["Field", "Value"], [
88
- ["Prompt", truncateCell(report.prompt || "(empty)", 100)],
89
- ["Efficiency", report.efficiencyScore == null ? "unknown" : `${report.efficiencyScore}%`],
90
- ["Changed files", report.changedFiles?.length ? report.changedFiles.join(", ") : "none detected"]
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(`Warning: ${warning}`);
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(section("Evidence Table"));
109
- lines.push(table(["#", "Status", "Score", "Kind", "Rule", "Evidence"], items.map((item, index) => [
110
- index + 1,
111
- item.status.toUpperCase(),
112
- typeof item.rule?.score === "number" ? item.rule.score.toFixed(2) : "",
113
- item.kind || "",
114
- truncateCell(item.rule?.content || "(missing rule)", 46),
115
- truncateCell(item.evidence || "(none)", 58)
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(`Matched line: ${where} ${truncate(line.content || "", 140)}`);
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
- lines.push(`${label}:`);
140
- for (const item of items.slice(0, 5)) {
141
- lines.push(`- Rule: ${truncate(item.rule.content, 68)}`);
142
- lines.push(` Evidence: ${truncate(item.evidence, 64)}`);
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
- if (items.length > 5) lines.push(`- ... ${items.length - 5} more`);
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, 8).map(formatRule)));
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, 8).map(formatRule)));
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
- const source = rule.sourcePath && rule.sourcePath !== "unknown" ? ` (${rule.sourcePath})` : "";
62
- return `- ${rule.content}${source}`;
60
+ return `- ${rule.content}`;
63
61
  }
64
62
 
65
63
  function formatSkill(skill) {
66
- const description = skill.description ? `: ${skill.description}` : "";
67
- const location = skill.path ? ` (${skill.path})` : "";
68
- return `- ${skill.name}${description}${location}`;
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
- const location = workflow.relativePath || workflow.path;
76
- const source = location ? `\n see: ${location}` : "";
77
- return `- ${name}${hint}${chain}${source}`;
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,