Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ jobs:
- run: git diff --exit-code schemas/

upstream-scan:
# Dry-run in CI so any drift shows up in the PR. This is not a gate —
# the scheduled upstream-sync workflow proposes fixes.
# Dry-run in CI so any drift surfaces in the PR logs. Not a gate, and not
# automated beyond this — run `/cc sync` by hand to triage the changelog
# and land a bump.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
35 changes: 0 additions & 35 deletions .github/workflows/upstream-sync.yml

This file was deleted.

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ All notable changes to cc-settings are documented here.

### Changed

- **Retired the automated upstream-sync cron in favor of manual `/cc sync`.** The daily GitHub Action (`.github/workflows/upstream-sync.yml`) and the `--open-pr` path in `src/upstream/scan.ts` only ever bumped a version number and dumped the drift string into a PR body. The scanner's `diffSets` compares the manifest against cc-settings' *own* zod schema — never upstream — and it never fetched the changelog, so it was structurally blind to new features, settings keys, env vars, hook events, and dedupe opportunities. That triage is the human-reviewed skill's job; the bot's PRs looked actionable but weren't, and raced the manual flow. Removing it also avoids a standing CI credential (making the cron *smarter* would have meant baking an `ANTHROPIC_API_KEY`/OAuth token into repo secrets and burning it unattended on every release).
- **Removed** — `.github/workflows/upstream-sync.yml`; `openSyncPr`/`saveManifest` and the `--open-pr` branch in `src/upstream/scan.ts` (now a pure dry-run detector, no `writeFile`/`runProcessFull` imports); stale "daily GH Action"/"bot owns this file" references in the manifest `description`/`source`, `skills/cc/SKILL.md`, `src/schemas/skill.ts`, and the `.github/workflows/ci.yml` `upstream-scan` comment.
- **Kept** — `bun run upstream:scan` (the dry-run detector) and its non-gating CI job, now the manual way to spot drift before running `/cc sync`.
- **Upstream sync to Claude Code 2.1.161.** Manifest bumped `2.1.160` → `2.1.161`. The release is otherwise all bug fixes / UI / perf with no cc-settings surface; the one tracked addition is `CLAUDE_CODE_TMPDIR` (surfaced by the `EADDRINUSE`/Unix-socket fix), added to the manifest `knownEnvVars` and the `docs/settings-reference.md` env-var table. `OTEL_RESOURCE_ATTRIBUTES` now feeds metric-datapoint labels but is a generic OTEL SDK var outside our CC-specific tracking convention — deliberately skipped.
- **Shared team-knowledge migrated from GitHub Project #7 to a markdown repo** (`darkroomengineering/team-knowledge`). The board was structurally hostile to its primary consumers (agents): network-gated GraphQL reads, not greppable offline, and no linter-enforced structure — `gh project item-create` never set the `Kind` field, so every `/share-learning` post landed `kind: None` and was invisible to a Kind-filtered query. The repo is one note per file + a generated `INDEX.md`, mirroring the local auto-memory tier. Decision + roadmap: `docs/plans/knowledge-repo-migration.md` (weighted comparison scored repo 795 vs board 515).
- **New** — `src/schemas/knowledge.ts` (zod frontmatter contract: `name`/`kind`/`tags`/`added-by`/`supersedes`), `src/lib/lint-knowledge.ts` + `src/scripts/lint-knowledge.ts` (`bun run lint:knowledge`), `src/scripts/new-note.ts` (`bun run new-note`), `tests/lint-knowledge.test.ts`, emitted `schemas/knowledge.schema.json`.
- **Changed** — `/share-learning` now reads `INDEX.md` for dedup and writes notes via `gh api` (was `gh project item-list`/`item-create`); `docs/knowledge-system.md`, the `AGENTS.md` Knowledge Routing section, and assorted docs retargeted; env `KNOWLEDGE_PROJECT_NUMBER` → `KNOWLEDGE_REPO`.
Expand Down
1 change: 1 addition & 0 deletions docs/settings-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Environment variables injected into every Claude Code session.
| `CLAUDE_CODE_ENABLE_AUTO_MODE` | `"1"` or unset | Opt in to auto mode on Bedrock, Vertex, and Foundry for Opus 4.7/4.8 (native on the first-party API) (v2.1.158) |
| `CLAUDE_CODE_SHELL_PREFIX` | shell prefix string | Custom shell prefix for MCP stdio server launches; overrides the default shell used to spawn stdio MCP processes (v2.1.128) |
| `CLAUDE_CODE_SUBAGENT_MODEL` | model shortname (e.g., `sonnet`) | Routes Agent Teams teammate subprocess sessions to a specific model; main session model is unaffected. cc-settings sets `sonnet` (v2.1.147) |
| `CLAUDE_CODE_TMPDIR` | directory path | Overrides the temp directory used for Unix sockets and scratch files; set it shallow to avoid `EADDRINUSE` from over-long socket paths (v2.1.161) |
| `OTEL_LOG_TOOL_DETAILS` | `"1"` or unset | Include custom/MCP command names in OTEL tool spans, and `tool_parameters` in `tool_decision` events; values are redacted unless this is set (v2.1.117; `tool_decision` params added v2.1.157) |

> **Note on `ultracode` mode (v2.1.154+)**: `/effort ultracode` is a Claude Code session-only mode that sends `xhigh` to the model AND has Claude plan a [dynamic workflow](https://code.claude.com/docs/en/workflows) for each substantive task. It is **not** a valid value for `CLAUDE_CODE_EFFORT_LEVEL`, the `effortLevel` setting, or the `--effort` flag — set it via `/effort ultracode` in-session, or pass `"ultracode": true` through `--settings` or an Agent SDK control request. Disable workflows entirely with `CLAUDE_CODE_DISABLE_WORKFLOWS=1` or `"disableWorkflows": true`.
Expand Down
6 changes: 4 additions & 2 deletions skills/cc/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ git log. Do not push if anything in Phase 7 is failing.

- It does not edit user-installed `~/.claude/settings.json`. cc-settings is the
source — users get the changes by re-running `setup.sh`.
- It does not auto-open PRs. The upstream-sync GitHub Action handles
manifest-only bumps; this skill is for the richer human-reviewed sync.
- It does not auto-open PRs, and there is no longer an automated cron. The
daily upstream-sync GitHub Action was retired — it could only bump the
version number, never triage the changelog. `bun run upstream:scan` is the
manual drift detector; this skill is the only sync path.
- It does not bump dependencies. That's a separate concern.

### Mental model
Expand Down
4 changes: 2 additions & 2 deletions src/schemas/skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export const SkillFrontmatter = z

// Allow unknown keys — the skills ecosystem is fast-moving and we'd rather
// accept an unrecognized field than reject a skill that works in newer CC.
// Contrast with settings.ts which is strict because drift there is the
// whole reason we have an upstream-sync bot.
// Contrast with settings.ts which is strict because drift there is what
// the `bun run upstream:scan` detector exists to catch.
})
.passthrough();

Expand Down
121 changes: 15 additions & 106 deletions src/upstream/scan.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
#!/usr/bin/env bun
// Upstream-sync scanner. Phases:
// dry-run (default): compares the current manifest against
// (a) npm's @anthropic-ai/claude-code latest version
// (b) the zod schema's enumerated keys (our local source of truth)
// and prints a delta.
// --open-pr: write an updated manifest + open a GitHub PR (requires `gh`
// CLI + a token with PR-write scope; used by .github/workflows/upstream-sync.yml).
// Upstream-sync scanner (dry-run detector). Compares the current manifest against
// (a) npm's @anthropic-ai/claude-code latest version
// (b) the zod schema's enumerated keys (our local source of truth)
// and prints the delta. Run it (`bun run upstream:scan`) to find out whether
// Claude Code has shipped a new release.
//
// Phase 2 adds the mutation path. It is deliberately low-blast-radius: the
// scanner only updates upstream/claude-code-manifest.json when the live
// npm version differs from the committed one. Schema edits stay human-
// reviewed — the PR body lists the delta and we let the reviewer wire the
// new keys into the zod schemas.

import { readFile, writeFile } from "node:fs/promises";
// The scanner never writes. The actual sync — changelog triage
// (ADOPT/DEDUPE/SKIP), schema + doc edits, and the manifest + CHANGELOG bumps —
// is the human-reviewed `/cc sync` skill, run on demand. (There was once a daily
// GH Action that auto-bumped the manifest version, but it could only ever change
// a version number — it never read the changelog — so it was retired in favor of
// the manual skill.)

import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { z } from "zod";
import { runProcessFull } from "../lib/git.ts";
import { HookEvent } from "../schemas/hooks.ts";
import { Settings } from "../schemas/settings.ts";

Expand Down Expand Up @@ -87,61 +85,13 @@ function diffSets(label: string, manifest: string[], live: string[]): string[] {
return out;
}

async function saveManifest(manifest: Manifest): Promise<void> {
// Preserve the existing on-disk key order as much as we can; stable keys
// keep diffs small when nothing substantive changed.
const content = `${JSON.stringify(manifest, null, 2)}\n`;
await writeFile(MANIFEST, content);
}

async function openSyncPr(summary: string, body: string, newVersion: string): Promise<void> {
const branch = `upstream-sync/${newVersion}-${Date.now()}`;
await runProcessFull("git", ["config", "user.name", "upstream-sync-bot"]);
await runProcessFull("git", [
"config",
"user.email",
"upstream-sync-bot@users.noreply.github.com",
]);
await runProcessFull("git", ["checkout", "-b", branch]);
await runProcessFull("git", ["add", "upstream/claude-code-manifest.json"]);
const commitTitle = `chore(upstream-sync): bump manifest to ${newVersion}`;
const commitRes = await runProcessFull("git", ["commit", "-m", commitTitle]);
if (commitRes.exit !== 0) {
console.error("git commit failed:", commitRes.stderr);
return;
}
const pushRes = await runProcessFull("git", ["push", "-u", "origin", branch]);
if (pushRes.exit !== 0) {
console.error("git push failed:", pushRes.stderr);
return;
}
const prRes = await runProcessFull("gh", [
"pr",
"create",
"--title",
summary,
"--body",
body,
"--label",
"upstream-sync",
]);
if (prRes.exit !== 0) {
console.error("gh pr create failed:", prRes.stderr);
return;
}
console.log(`opened PR for ${newVersion}: ${prRes.stdout.trim()}`);
}

async function main() {
const args = process.argv.slice(2);
const openPr = args.includes("--open-pr");

const manifest = await loadManifest();
const liveSettingsKeys = settingsKeysFromSchema();
const liveHookEvents = hookEventsFromSchema();
const liveVersion = await fetchLatestClaudeCodeVersion();

console.log(`cc-settings upstream scan (${openPr ? "open-pr" : "dry-run"})`);
console.log("cc-settings upstream scan");
console.log(` manifest version: ${manifest.claudeCodeVersion} (scanned ${manifest.lastScan})`);
console.log(` live version: ${liveVersion ?? "<unknown — network offline>"}`);

Expand All @@ -161,48 +111,7 @@ async function main() {

console.log("\ndrift detected:");
for (const line of findings) console.log(line);

if (!openPr) {
console.log("\nre-run with --open-pr to propose the manifest update as a PR.");
return;
}

if (!versionDrift) {
// Schema-only drift is interesting but we don't auto-edit schemas; surface
// via CI logs and let a human wire the new keys.
console.log("\nschema-only drift — no manifest update proposed. Review the findings above.");
return;
}

// Mutate the manifest version + lastScan, keep key lists untouched (schema
// additions are human work). If schema keys drift too, we note them in the
// PR body.
const updated: Manifest = {
...manifest,
claudeCodeVersion: versionDrift,
lastScan: new Date().toISOString(),
};
await saveManifest(updated);

const summary = `chore(upstream-sync): bump claude-code manifest to ${versionDrift}`;
const body = [
"Automated upstream-sync PR.",
"",
`- manifest: \`${manifest.claudeCodeVersion}\` → \`${versionDrift}\``,
"",
"## Findings",
"",
"```",
findings.join("\n"),
"```",
"",
"If the findings list schema-key drift, wire the new keys into the appropriate",
"zod schemas in `src/schemas/` and push onto this branch.",
"",
"Generated by `src/upstream/scan.ts --open-pr`.",
].join("\n");

await openSyncPr(summary, body, versionDrift);
console.log("\nrun `/cc sync` to triage the changelog and land the bump.");
}

main().catch((err) => {
Expand Down
9 changes: 5 additions & 4 deletions upstream/claude-code-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"lastScan": "2026-06-02T00:00:00.000Z",
"claudeCodeVersion": "2.1.160",
"lastScan": "2026-06-03T00:00:00.000Z",
"claudeCodeVersion": "2.1.161",
"knownSettingsKeys": [
"$schema",
"agent",
Expand Down Expand Up @@ -167,6 +167,7 @@
"CLAUDE_CODE_STOP_HOOK_BLOCK_CAP",
"CLAUDE_CODE_SUBAGENT_MODEL",
"CLAUDE_CODE_SUBPROCESS_ENV_SCRUB",
"CLAUDE_CODE_TMPDIR",
"CLAUDE_CODE_USE_POWERSHELL_TOOL",
"CLAUDE_EFFORT",
"DISABLE_UPDATES",
Expand All @@ -193,9 +194,9 @@
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cc-settings.darkroom/upstream/claude-code-manifest.json",
"description": "Snapshot of Claude Code upstream surface area as known to the cc-settings zod schemas. Updated by src/upstream/scan.ts (daily GH Action). Drift triggers a PR that extends the schemas and this manifest atomically. See docs/migration-coexistence.md and ~/Desktop/cc-settings-MIGRATION.md v2.",
"description": "Snapshot of Claude Code upstream surface area as known to the cc-settings zod schemas. Updated by hand via the /cc sync skill; bun run upstream:scan reports drift but never writes. See docs/migration-coexistence.md.",
"manifestSchemaVersion": 1,
"source": "manual sync (cc-settings v11.0.1). From 2.1.115 onward, upstream-sync bot owns this file.",
"source": "manual sync via the /cc sync skill (the automated upstream-sync bot was retired June 2026).",
"knownPermissionModes": [
"default",
"acceptEdits",
Expand Down
Loading