oh-my-customcode 1.1.100 → 1.1.102
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/dist/cli/index.js +331 -196
- package/dist/index.js +71 -32
- package/package.json +1 -1
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +24 -1
- package/templates/guides/claude-code/15-version-compatibility.md +148 -1
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +24 -1
package/dist/index.js
CHANGED
|
@@ -206,7 +206,7 @@ var init_fs = () => {};
|
|
|
206
206
|
|
|
207
207
|
// src/utils/home.ts
|
|
208
208
|
import { homedir, userInfo } from "node:os";
|
|
209
|
-
import { isAbsolute as isAbsolute2, parse, sep as sep2 } from "node:path";
|
|
209
|
+
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute2, join as join6, parse, sep as sep2 } from "node:path";
|
|
210
210
|
function readEnvHome() {
|
|
211
211
|
return process.env.HOME;
|
|
212
212
|
}
|
|
@@ -246,7 +246,7 @@ var init_home = () => {};
|
|
|
246
246
|
import { realpathSync } from "node:fs";
|
|
247
247
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
248
248
|
import { tmpdir } from "node:os";
|
|
249
|
-
import { basename as
|
|
249
|
+
import { basename as basename4, join as join7, resolve as resolve2, sep as sep3 } from "node:path";
|
|
250
250
|
function isTempPath(projectPath) {
|
|
251
251
|
const normalized = resolve2(projectPath);
|
|
252
252
|
const candidates = new Set;
|
|
@@ -281,10 +281,10 @@ function registryDir(home) {
|
|
|
281
281
|
if (resolved === "") {
|
|
282
282
|
throw new Error("Could not determine the home directory for the project registry");
|
|
283
283
|
}
|
|
284
|
-
return
|
|
284
|
+
return join7(resolved, ".oh-my-customcode");
|
|
285
285
|
}
|
|
286
286
|
function registryPath() {
|
|
287
|
-
return
|
|
287
|
+
return join7(registryDir(), "projects.json");
|
|
288
288
|
}
|
|
289
289
|
function emptyRegistry() {
|
|
290
290
|
return { projects: {} };
|
|
@@ -2132,6 +2132,10 @@ async function collectFiles(dir, projectRoot, isTopLevel) {
|
|
|
2132
2132
|
}
|
|
2133
2133
|
return results;
|
|
2134
2134
|
}
|
|
2135
|
+
function sortFilesByPath(files) {
|
|
2136
|
+
const sorted = Object.keys(files).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
2137
|
+
return Object.fromEntries(sorted.map((key) => [key, files[key]]));
|
|
2138
|
+
}
|
|
2135
2139
|
async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
2136
2140
|
const files = {};
|
|
2137
2141
|
const componentRoots = COMPONENT_PATHS.map(([prefix]) => join4(targetDir, prefix));
|
|
@@ -2168,15 +2172,50 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
|
2168
2172
|
generatorVersion,
|
|
2169
2173
|
generatedAt: new Date().toISOString(),
|
|
2170
2174
|
templateVersion,
|
|
2171
|
-
files
|
|
2175
|
+
files: sortFilesByPath(files)
|
|
2172
2176
|
};
|
|
2173
2177
|
}
|
|
2178
|
+
function hasSameLockfileContent(next, previous) {
|
|
2179
|
+
if (next.lockfileVersion !== previous.lockfileVersion || next.generatorVersion !== previous.generatorVersion || next.templateVersion !== previous.templateVersion) {
|
|
2180
|
+
return false;
|
|
2181
|
+
}
|
|
2182
|
+
const nextKeys = Object.keys(next.files);
|
|
2183
|
+
if (nextKeys.length !== Object.keys(previous.files).length) {
|
|
2184
|
+
return false;
|
|
2185
|
+
}
|
|
2186
|
+
return nextKeys.every((key) => {
|
|
2187
|
+
if (!Object.hasOwn(previous.files, key)) {
|
|
2188
|
+
return false;
|
|
2189
|
+
}
|
|
2190
|
+
const before = previous.files[key];
|
|
2191
|
+
const after = next.files[key];
|
|
2192
|
+
return typeof before === "object" && before !== null && before.templateHash === after.templateHash && before.size === after.size && before.component === after.component;
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
function isIsoTimestamp(value) {
|
|
2196
|
+
if (typeof value !== "string") {
|
|
2197
|
+
return false;
|
|
2198
|
+
}
|
|
2199
|
+
const time = new Date(value).getTime();
|
|
2200
|
+
return !Number.isNaN(time) && new Date(time).toISOString() === value;
|
|
2201
|
+
}
|
|
2202
|
+
function preserveGeneratedAt(next, previous) {
|
|
2203
|
+
if (previous === null || !isIsoTimestamp(previous.generatedAt)) {
|
|
2204
|
+
return next;
|
|
2205
|
+
}
|
|
2206
|
+
if (!hasSameLockfileContent(next, previous)) {
|
|
2207
|
+
return next;
|
|
2208
|
+
}
|
|
2209
|
+
return { ...next, generatedAt: previous.generatedAt };
|
|
2210
|
+
}
|
|
2174
2211
|
async function generateAndWriteLockfileForDir(targetDir) {
|
|
2175
2212
|
try {
|
|
2176
2213
|
const packageRoot = getPackageRoot();
|
|
2177
2214
|
const manifest = await readJsonFile(join4(packageRoot, "templates", "manifest.json"));
|
|
2178
2215
|
const { version: generatorVersion } = await readJsonFile(join4(packageRoot, "package.json"));
|
|
2179
|
-
const
|
|
2216
|
+
const generated = await generateLockfile(targetDir, generatorVersion, manifest.version);
|
|
2217
|
+
const previous = await readLockfile(targetDir);
|
|
2218
|
+
const lockfile = preserveGeneratedAt(generated, previous);
|
|
2180
2219
|
await writeLockfile(targetDir, lockfile);
|
|
2181
2220
|
return { fileCount: Object.keys(lockfile.files).length };
|
|
2182
2221
|
} catch (err) {
|
|
@@ -2807,14 +2846,14 @@ async function detectProvider(_options = {}) {
|
|
|
2807
2846
|
};
|
|
2808
2847
|
}
|
|
2809
2848
|
// src/core/updater.ts
|
|
2810
|
-
import { join as
|
|
2849
|
+
import { join as join8 } from "node:path";
|
|
2811
2850
|
// package.json
|
|
2812
2851
|
var package_default = {
|
|
2813
2852
|
name: "oh-my-customcode",
|
|
2814
2853
|
workspaces: [
|
|
2815
2854
|
"packages/*"
|
|
2816
2855
|
],
|
|
2817
|
-
version: "1.1.
|
|
2856
|
+
version: "1.1.102",
|
|
2818
2857
|
description: "Batteries-included agent harness for Claude Code",
|
|
2819
2858
|
type: "module",
|
|
2820
2859
|
bin: {
|
|
@@ -5587,7 +5626,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
5587
5626
|
}
|
|
5588
5627
|
async function updateEntryDoc(targetDir, config, options) {
|
|
5589
5628
|
const layout = getProviderLayout();
|
|
5590
|
-
const entryPath =
|
|
5629
|
+
const entryPath = join8(targetDir, layout.entryFile);
|
|
5591
5630
|
const templateName = getEntryTemplateName2(config.language);
|
|
5592
5631
|
const templatePath = resolveTemplatePath(templateName);
|
|
5593
5632
|
if (!await fileExists(templatePath)) {
|
|
@@ -5625,7 +5664,7 @@ async function backfillStatusLineRefreshInterval(targetDir, options) {
|
|
|
5625
5664
|
return;
|
|
5626
5665
|
}
|
|
5627
5666
|
const layout = getProviderLayout();
|
|
5628
|
-
const settingsPath =
|
|
5667
|
+
const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
|
|
5629
5668
|
if (!await fileExists(settingsPath)) {
|
|
5630
5669
|
return;
|
|
5631
5670
|
}
|
|
@@ -5651,7 +5690,7 @@ async function migrateHookCommandsInSettingsLocal(targetDir, options) {
|
|
|
5651
5690
|
return;
|
|
5652
5691
|
}
|
|
5653
5692
|
const layout = getProviderLayout();
|
|
5654
|
-
const settingsPath =
|
|
5693
|
+
const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
|
|
5655
5694
|
if (!await fileExists(settingsPath)) {
|
|
5656
5695
|
return;
|
|
5657
5696
|
}
|
|
@@ -5746,7 +5785,7 @@ async function regenerateLockfile(targetDir, result) {
|
|
|
5746
5785
|
}
|
|
5747
5786
|
}
|
|
5748
5787
|
async function shouldSkipSelfUpdate(targetDir, result) {
|
|
5749
|
-
const targetPkgPath =
|
|
5788
|
+
const targetPkgPath = join8(targetDir, "package.json");
|
|
5750
5789
|
if (await fileExists(targetPkgPath)) {
|
|
5751
5790
|
const targetPkg = await readJsonFile(targetPkgPath);
|
|
5752
5791
|
if (targetPkg.name === "oh-my-customcode") {
|
|
@@ -5839,8 +5878,8 @@ async function checkForUpdates(targetDir) {
|
|
|
5839
5878
|
async function applyUpdates(targetDir, updates) {
|
|
5840
5879
|
const fs = await import("node:fs/promises");
|
|
5841
5880
|
for (const update of updates) {
|
|
5842
|
-
const fullPath =
|
|
5843
|
-
await ensureDirectory(
|
|
5881
|
+
const fullPath = join8(targetDir, update.path);
|
|
5882
|
+
await ensureDirectory(join8(fullPath, ".."));
|
|
5844
5883
|
await fs.writeFile(fullPath, update.content, "utf-8");
|
|
5845
5884
|
debug("update.file_applied", { path: update.path });
|
|
5846
5885
|
}
|
|
@@ -5849,7 +5888,7 @@ async function preserveCustomizations(targetDir, customizations) {
|
|
|
5849
5888
|
const preserved = new Map;
|
|
5850
5889
|
const fs = await import("node:fs/promises");
|
|
5851
5890
|
for (const filePath of customizations) {
|
|
5852
|
-
const fullPath =
|
|
5891
|
+
const fullPath = join8(targetDir, filePath);
|
|
5853
5892
|
if (await fileExists(fullPath)) {
|
|
5854
5893
|
const content = await fs.readFile(fullPath, "utf-8");
|
|
5855
5894
|
preserved.set(filePath, content);
|
|
@@ -5906,11 +5945,11 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
5906
5945
|
const warnedPaths = [];
|
|
5907
5946
|
const updatedPaths = [];
|
|
5908
5947
|
for (const p of protectedRelative) {
|
|
5909
|
-
const targetFilePath =
|
|
5948
|
+
const targetFilePath = join8(targetDir, componentPath, p);
|
|
5910
5949
|
const lockfileKey = `${componentPath}/${p}`.replace(/\\/g, "/");
|
|
5911
5950
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
5912
5951
|
if (shouldSkip) {
|
|
5913
|
-
skipPaths.push(path.relative(destPath,
|
|
5952
|
+
skipPaths.push(path.relative(destPath, join8(destPath, p)));
|
|
5914
5953
|
warnedPaths.push(p);
|
|
5915
5954
|
} else {
|
|
5916
5955
|
updatedPaths.push(p);
|
|
@@ -5956,7 +5995,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
5956
5995
|
const preservedFiles = [];
|
|
5957
5996
|
const componentPath = getComponentPath2(component);
|
|
5958
5997
|
const srcPath = resolveTemplatePath(componentPath);
|
|
5959
|
-
const destPath =
|
|
5998
|
+
const destPath = join8(targetDir, componentPath);
|
|
5960
5999
|
const customComponents = config.customComponents || [];
|
|
5961
6000
|
const skipPaths = [];
|
|
5962
6001
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -5998,7 +6037,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
5998
6037
|
}
|
|
5999
6038
|
skipPaths.push(...protectedSkipPaths);
|
|
6000
6039
|
const path = await import("node:path");
|
|
6001
|
-
const normalizedSkipPaths = skipPaths.map((p) => path.relative(destPath,
|
|
6040
|
+
const normalizedSkipPaths = skipPaths.map((p) => path.relative(destPath, join8(targetDir, p)));
|
|
6002
6041
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
6003
6042
|
await copyDirectory(srcPath, destPath, {
|
|
6004
6043
|
overwrite: true,
|
|
@@ -6027,7 +6066,7 @@ async function syncRootLevelFiles(targetDir, options, customizations) {
|
|
|
6027
6066
|
const preserved = [];
|
|
6028
6067
|
const preserveSet = new Set(customizations && !options.forceOverwriteAll ? customizations.preserveFiles : []);
|
|
6029
6068
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
6030
|
-
const srcPath = resolveTemplatePath(
|
|
6069
|
+
const srcPath = resolveTemplatePath(join8(layout.rootDir, fileName));
|
|
6031
6070
|
if (!await fileExists(srcPath)) {
|
|
6032
6071
|
continue;
|
|
6033
6072
|
}
|
|
@@ -6036,8 +6075,8 @@ async function syncRootLevelFiles(targetDir, options, customizations) {
|
|
|
6036
6075
|
preserved.push(relPath);
|
|
6037
6076
|
continue;
|
|
6038
6077
|
}
|
|
6039
|
-
const destPath =
|
|
6040
|
-
await ensureDirectory(
|
|
6078
|
+
const destPath = join8(targetDir, layout.rootDir, fileName);
|
|
6079
|
+
await ensureDirectory(join8(destPath, ".."));
|
|
6041
6080
|
await fs.copyFile(srcPath, destPath);
|
|
6042
6081
|
if (fileName.endsWith(".sh")) {
|
|
6043
6082
|
await fs.chmod(destPath, 493);
|
|
@@ -6072,7 +6111,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
6072
6111
|
});
|
|
6073
6112
|
continue;
|
|
6074
6113
|
}
|
|
6075
|
-
const fullPath =
|
|
6114
|
+
const fullPath = join8(targetDir, entry.path);
|
|
6076
6115
|
if (await fileExists(fullPath)) {
|
|
6077
6116
|
await fs.unlink(fullPath);
|
|
6078
6117
|
removed.push(entry.path);
|
|
@@ -6113,7 +6152,7 @@ async function syncNamespaceInFile(targetFilePath, upstreamFilePath) {
|
|
|
6113
6152
|
async function processNamespaceSyncEntry(entry, relPath, fullSrcPath, destPath, componentPath, lockfile) {
|
|
6114
6153
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
6115
6154
|
return null;
|
|
6116
|
-
const targetFilePath =
|
|
6155
|
+
const targetFilePath = join8(destPath, relPath);
|
|
6117
6156
|
const lockfileKey = `${componentPath}/${relPath}`.replace(/\\/g, "/");
|
|
6118
6157
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
6119
6158
|
if (shouldSkip)
|
|
@@ -6128,7 +6167,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
6128
6167
|
return [];
|
|
6129
6168
|
const componentPath = getComponentPath2(component);
|
|
6130
6169
|
const srcPath = resolveTemplatePath(componentPath);
|
|
6131
|
-
const destPath =
|
|
6170
|
+
const destPath = join8(targetDir, componentPath);
|
|
6132
6171
|
const fs = await import("node:fs/promises");
|
|
6133
6172
|
const synced = [];
|
|
6134
6173
|
const queue = [{ dir: srcPath, relDir: "" }];
|
|
@@ -6142,7 +6181,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
6142
6181
|
}
|
|
6143
6182
|
for (const entry of entries) {
|
|
6144
6183
|
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
6145
|
-
const fullSrcPath =
|
|
6184
|
+
const fullSrcPath = join8(dir, entry.name);
|
|
6146
6185
|
if (entry.isDirectory()) {
|
|
6147
6186
|
queue.push({ dir: fullSrcPath, relDir: relPath });
|
|
6148
6187
|
continue;
|
|
@@ -6165,26 +6204,26 @@ function getComponentPath2(component) {
|
|
|
6165
6204
|
}
|
|
6166
6205
|
async function backupInstallation(targetDir) {
|
|
6167
6206
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
6168
|
-
const backupDir =
|
|
6207
|
+
const backupDir = join8(targetDir, `.omcustom-backup-${timestamp}`);
|
|
6169
6208
|
const fs = await import("node:fs/promises");
|
|
6170
6209
|
await ensureDirectory(backupDir);
|
|
6171
6210
|
const layout = getProviderLayout();
|
|
6172
6211
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
6173
6212
|
for (const dir of dirsToBackup) {
|
|
6174
|
-
const srcPath =
|
|
6213
|
+
const srcPath = join8(targetDir, dir);
|
|
6175
6214
|
if (await fileExists(srcPath)) {
|
|
6176
|
-
const destPath =
|
|
6215
|
+
const destPath = join8(backupDir, dir);
|
|
6177
6216
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
6178
6217
|
}
|
|
6179
6218
|
}
|
|
6180
|
-
const entryPath =
|
|
6219
|
+
const entryPath = join8(targetDir, layout.entryFile);
|
|
6181
6220
|
if (await fileExists(entryPath)) {
|
|
6182
|
-
await fs.copyFile(entryPath,
|
|
6221
|
+
await fs.copyFile(entryPath, join8(backupDir, layout.entryFile));
|
|
6183
6222
|
}
|
|
6184
6223
|
return backupDir;
|
|
6185
6224
|
}
|
|
6186
6225
|
async function loadCustomizationManifest(targetDir) {
|
|
6187
|
-
const manifestPath =
|
|
6226
|
+
const manifestPath = join8(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
6188
6227
|
if (await fileExists(manifestPath)) {
|
|
6189
6228
|
return readJsonFile(manifestPath);
|
|
6190
6229
|
}
|
package/package.json
CHANGED
|
@@ -171,8 +171,28 @@ steps:
|
|
|
171
171
|
- Otherwise: highest-priority open tier (bug → chore → feature)
|
|
172
172
|
2. Apply Step 1 filter (label-based exclusion and preference)
|
|
173
173
|
3. Sort by dependency: prerequisites before dependents
|
|
174
|
-
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers)
|
|
174
|
+
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers) —
|
|
175
|
+
EXCEPT the batch-size fill in rule 6, which takes precedence when the top tier has < 3.
|
|
175
176
|
5. Minimum 1 issue; if 0 eligible, halt with "no eligible issues for auto-dev run"
|
|
177
|
+
6. Batch-size norm (fill exception to "don't mix tiers"): if the top tier has fewer than
|
|
178
|
+
3 eligible issues, fill from the next tiers in order (bug → chore → feature, then
|
|
179
|
+
preferred-label / retrospective-proposal issues) until the scope is 3-7 issues. Tier
|
|
180
|
+
mixing is allowed ONLY for this fill. A 1-2 issue release is permitted ONLY when the
|
|
181
|
+
eligible total across ALL tiers is < 3. Issues split off or deferred pending approval
|
|
182
|
+
or a decision (`.claude/hooks/**` unattended split in Step 3, decision-needed, etc.)
|
|
183
|
+
do NOT count toward the eligible total. Because the Step 3 split happens AFTER this
|
|
184
|
+
step, re-evaluate this fill after the Step 3 split: if the remaining scope is below 3
|
|
185
|
+
and eligible issues remain, fill again from the next tiers, then re-run the Step 3 path
|
|
186
|
+
pre-check on any newly added issue. When the scope has multiple issues, issue
|
|
187
|
+
implementation delegations in parallel (R009) unless their target files overlap, or one
|
|
188
|
+
delegation depends on another's in-release implementation choice (see the implement
|
|
189
|
+
step's R009 「File-Disjoint ≠ Independent」 document-implementation variant); local git
|
|
190
|
+
state changes (per-issue lifecycle commits via mgr-gitnerd) remain serialized, one at a
|
|
191
|
+
time (R009 「File-Disjoint ≠ Independent (Local Git State)」).
|
|
192
|
+
Rationale: the fixed per-release cost (review rounds, CI, release.yml) must not be
|
|
193
|
+
carried by 1-2 issues; consecutive top-tier-only runs also starve lower tiers.
|
|
194
|
+
Origin: user feedback 2026-10-05 ("이슈를 너무 조금씩 해결한다"); #1814 harness proposal R3
|
|
195
|
+
(lower-tier starvation).
|
|
176
196
|
|
|
177
197
|
## Step 3 — Approval-required path pre-check (R010)
|
|
178
198
|
|
|
@@ -211,6 +231,9 @@ steps:
|
|
|
211
231
|
|
|
212
232
|
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
213
233
|
|
|
234
|
+
After an unattended split, re-evaluate the Step 2 rule 6 batch-size fill against the
|
|
235
|
+
remaining scope (split-off issues do not count) before assigning the milestone.
|
|
236
|
+
|
|
214
237
|
Assign scoped issues to milestone.
|
|
215
238
|
Output markdown release manifest:
|
|
216
239
|
| order | # | title | prerequisite | effort | labels |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Claude Code Version Compatibility
|
|
2
2
|
|
|
3
|
-
> Updated: 2026-
|
|
3
|
+
> Updated: 2026-10-05
|
|
4
4
|
> Source: Claude Code release notes (#967, #968, #969, #1126 auto-detected by claude-native skill, #1137, #1158, #1242, #1243, #1244, #1245, #1276, #1280, #1713, #1714, #1716, #1746, #1747)
|
|
5
5
|
>
|
|
6
6
|
> **Note (compat 노트 이관, v1.1.9~v1.1.76)**: v2.1.161~v2.1.276 구간의 CC 호환성 노트는 `.claude/rules/` 각 규칙(R001/R002/R006/R010/R012 등)에 인라인으로 축적되어 있으며, 이 구간은 그대로 보존합니다.
|
|
@@ -1757,6 +1757,146 @@ Opus 4.8에서 thinking blocks가 수정되어 API 오류가 발생하던 버그
|
|
|
1757
1757
|
|
|
1758
1758
|
---
|
|
1759
1759
|
|
|
1760
|
+
## v2.1.288 (2026-10-02)
|
|
1761
|
+
|
|
1762
|
+
> Issue: #1790 — Claude Code v2.1.288 compatibility documentation
|
|
1763
|
+
> Scope-ceiling check (R017): 조사 시점(2026-10-05)의 최신 릴리즈는 2.1.289입니다(`claude --version`=2.1.289, `npm view @anthropic-ai/claude-code version`=2.1.289). 2.1.289 CHANGELOG에는 "[VSCode] Reverted a 2.1.288 change to `claude auth status` that may have made sign-outs more frequent"가 있으나, 2.1.288 CHANGELOG 절에는 `claude auth status` 문구가 없어(`grep -F` 0건) 되돌려진 288 항목을 특정할 수 없고 VSCode 전용이므로 아래 항목의 롤백은 확인되지 않았습니다. 릴리즈 게시 시각은 2026-10-02T20:19:57Z입니다(`gh release view v2.1.288 --repo anthropics/claude-code --json publishedAt`).
|
|
1764
|
+
|
|
1765
|
+
### 세션 · 재개 · 컨텍스트
|
|
1766
|
+
|
|
1767
|
+
- CHANGELOG 원문: "Fixed mid-response API timeouts failing the turn: non-interactive sessions and subagents now continue from the partial response, and thinking-only responses are retried"
|
|
1768
|
+
(288) 응답 도중 API 타임아웃이 턴을 실패시키던 결함이 수정되어, 비대화형 세션과 서브에이전트가 부분 응답에서 이어가고 thinking만 있는 응답은 재시도됩니다. R004 Retryable 재시도 전략을 플랫폼이 서브에이전트 경로에서도 수행한다는 (257)·(246)의 자동 이어감과 같은 계열이므로, 위임 에이전트의 중간 종료를 진단할 때 네트워크 절단 축은 후순위로 두고 R020의 maxTurns 한도를 먼저 확인하는 기존 순서를 유지합니다. 룰 변경은 없습니다.
|
|
1769
|
+
- CHANGELOG 원문: "Fixed long conversations failing with "Prompt is too long" instead of auto-compacting when the last reply reported zero token usage"
|
|
1770
|
+
(288) 마지막 응답이 토큰 사용량 0을 보고하면 긴 대화가 auto-compact 대신 "Prompt is too long"으로 실패하던 결함이 수정되었습니다. R013 컨텍스트 예산·(269)(274)의 "Prompt is too long" 계열 수정과 같은 맥락이며 임계값 정의는 바뀌지 않습니다. `/fsd` 같은 장기 자율 루프에 유리한 수정입니다.
|
|
1771
|
+
- CHANGELOG 원문: "Fixed `--resume` sometimes dropping files and other context that a compaction had just restored"
|
|
1772
|
+
(288) `--resume`이 compaction 직후 복원된 파일과 컨텍스트를 간혹 누락하던 결함이 수정되었습니다. 압축·재시작 뒤 CLAUDE.md를 다시 읽는 R010의 Session Continuity 절차는 그대로 유지하며(재개 관련 버전 노트는 R011이 담당합니다), 이 수정은 구버전 재개 세션의 컨텍스트 누락을 모델 규칙 위반으로만 귀속하지 않는 근거가 됩니다.
|
|
1773
|
+
- CHANGELOG 원문: "Fixed a resumed session sometimes not saving the last response of a turn, so that the next `--resume` showed the prompt unanswered"
|
|
1774
|
+
(288) 재개된 세션이 간혹 턴의 마지막 응답을 저장하지 못해 다음 `--resume`에서 프롬프트가 미응답으로 보이던 결함이 수정되었습니다. 미응답처럼 보이지만 실제로는 응답이 있었을 수 있는 역방향 사례이므로 R020의 Failure/Interrupt Report ≠ Actual Failure (reverse direction) 조항대로, 재개 뒤 미응답으로 보이는 턴은 트랜스크립트 표시가 아니라 산출물 실측으로 판정합니다.
|
|
1775
|
+
- CHANGELOG 원문: "Fixed resume occasionally loading a transcript cut short when the same session rewrote the file during the load"
|
|
1776
|
+
(288) 같은 세션이 로드 중 파일을 다시 쓰면 재개가 간혹 잘린 트랜스크립트를 읽던 결함이 수정되었습니다. 트랜스크립트 계수(`scripts/count-r007-r008.sh`, R023)의 입력 신뢰도에 관한 맥락 정보입니다. R011의 (275) 노트는 275 이전에 malformed 엔트리 하나 때문에 재개에 실패한 세션을 대상으로 R020 트랜스크립트 계수를 하한값으로 취급하도록 하며, 잘린 트랜스크립트를 읽은 세션의 계수도 하한값으로 보는 것이 같은 방향입니다 [가설 — 잘린 로드 세션의 계수 보정은 (275) 노트가 직접 다루지 않는 유비]. 룰 변경은 없습니다.
|
|
1777
|
+
- CHANGELOG 원문: "Fixed resuming a conversation started on 2.1.286 or earlier dropping the model's earlier thinking"
|
|
1778
|
+
(288) 2.1.286 이하에서 시작한 대화를 재개하면 모델의 이전 thinking이 누락되던 결함이 수정되었습니다. 맥락 정보이며 영향은 확인되지 않았습니다.
|
|
1779
|
+
- CHANGELOG 원문: "Fixed session titles, memory recall and prompt hooks failing on Mantle or behind gateways that reject structured outputs; added `CLAUDE_CODE_DISABLE_STRUCTURED_OUTPUTS` to turn structured outputs off"
|
|
1780
|
+
(288) Mantle이나 structured outputs를 거부하는 게이트웨이 뒤에서 세션 제목·메모리 recall·prompt 훅이 실패하던 결함이 수정되었고, `CLAUDE_CODE_DISABLE_STRUCTURED_OUTPUTS`로 structured outputs를 끌 수 있습니다. 관측한 범위에서 이 저장소에는 `type: prompt` 훅이 PostCompact(matcher `*`) 1개 있으나, 프로젝트 `.claude/settings.json`에 `env`가 없고(`.claude/settings.local.json`의 env 키는 `CLAUDE_COST_CAP`뿐) Mantle·게이트웨이 설정이 관측되지 않아 영향이 없습니다.
|
|
1781
|
+
- CHANGELOG 원문: "Changed `/autocompact` to save the auto-compact window per model, so each model keeps its own setting when you switch"
|
|
1782
|
+
(288) `/autocompact`가 auto-compact 창을 모델별로 저장하므로 모델을 바꿔도 각 모델이 자기 설정을 유지합니다. R013 임계값은 창 대비 백분율이므로 모델 전환 시 절대 토큰량이 모델별 설정에 따라 달라질 수 있습니다 [가설 — 저장소에서 /autocompact 사용 여부 미관측].
|
|
1783
|
+
- CHANGELOG 원문: "Fixed the first request in a fresh environment or after a model switch using the built-in output limit and auto-compact window, not the server's; that request may now wait up to 1.5 seconds"
|
|
1784
|
+
(288) 새 환경이나 모델 전환 뒤 첫 요청이 서버의 출력 한도·auto-compact 창 대신 내장값을 쓰던 결함이 수정되었고, 그 요청은 이제 최대 1.5초 기다릴 수 있습니다. 위 `/autocompact` 항목과 함께 R013 컨텍스트 예산의 절대 토큰량 해석에 관한 맥락 정보이며, 임계값 정의는 바뀌지 않습니다.
|
|
1785
|
+
- CHANGELOG 원문: "Fixed unattended sessions (`CLAUDE_CODE_RETRY_WATCHDOG`) retrying for hours after a very long response stream failed; Claude Code now streams again, and gives up after three timeouts"
|
|
1786
|
+
(288) `CLAUDE_CODE_RETRY_WATCHDOG`을 쓰는 무인 세션이 매우 긴 응답 스트림 실패 뒤 몇 시간씩 재시도하던 결함이 수정되어, 이제 다시 스트리밍하고 타임아웃 세 번 뒤 포기합니다. R018의 (260) 노트가 이 환경 변수를 예시로 언급합니다. (288) 타임아웃 세 번 뒤 포기하는 동작은 R004의 최대 3회 재시도 후 보고하는 전략과 방향이 같습니다. (288) 저장소에서 이 환경 변수 사용은 관측되지 않았습니다.
|
|
1787
|
+
- CHANGELOG 원문: "Fixed headless (`-p` / SDK) sessions occasionally ignoring SIGTERM when a supervisor such as `timeout` or systemd sends SIGCONT alongside it"
|
|
1788
|
+
(288) 감독자(`timeout`, systemd 등)가 SIGCONT를 함께 보낼 때 headless(`-p` / SDK) 세션이 간혹 SIGTERM을 무시하던 결함이 수정되었습니다. 관측한 범위에서 `infra/hada-scout/scout-runner.sh`가 `timeout "${SCOUT_TIMEOUT}" claude -p`(`-k` 없음)로 실행하므로 해당 구성입니다. 288 이전에는 SIGTERM이 간혹 무시되어 실행이 끝나지 않을 수 있었다는 회고적 함의가 있습니다 [가설 — 실제 배포 여부와 이미지의 CC 버전 미실측]. 룰 변경은 없습니다.
|
|
1789
|
+
|
|
1790
|
+
### 권한 · auto mode · rm
|
|
1791
|
+
|
|
1792
|
+
- CHANGELOG 원문: "Fixed auto mode denials pointing Claude at a Bash permission rule when the blocked tool was not Bash"
|
|
1793
|
+
(288) 차단된 도구가 Bash가 아닌데도 auto mode 거부 메시지가 Bash 권한 규칙을 가리키던 결함이 수정되었습니다. 관측한 범위에서 사용자 scope 유효 permission mode는 `auto`(`jq -r '.permissions.defaultMode // "unset"' ~/.claude/settings.json`)이므로 직접 관련됩니다. R001의 classifier 차단 후 재시도 금지 취지와 R010 STOP Protocol의 진단 정확도가 높아질 뿐 규범은 그대로입니다.
|
|
1794
|
+
- CHANGELOG 원문: "Improved auto mode: when a conversation grows too long for the client-side safety classifier to review, it is now compacted instead of prompting for, or failing, every tool call"
|
|
1795
|
+
(288) auto mode에서 대화가 클라이언트 측 안전 classifier가 검토하기에 너무 길어지면 모든 도구 호출을 묻거나 실패시키는 대신 compact됩니다. 유효 모드가 `auto`인 이 저장소의 긴 세션에 해당합니다. 구버전의 긴 세션에서 도구 호출 거부·프롬프트가 있었다면 모델 규칙 위반으로만 귀속할 수 없습니다 [가설 — 해당 거부·프롬프트가 실제로 있었는지 미관측]. R002·R021 문안 변경은 없습니다.
|
|
1796
|
+
- CHANGELOG 원문: "Changed the client-side auto mode classifier to ignore an `ANTHROPIC_DEFAULT_SONNET_MODEL` pin that names Claude Sonnet 5.5 or Opus 5.5 and use Claude Sonnet 5 instead"
|
|
1797
|
+
(288) 클라이언트 측 auto mode classifier는 Claude Sonnet 5.5 또는 Opus 5.5를 가리키는 `ANTHROPIC_DEFAULT_SONNET_MODEL` 고정을 무시하고 Claude Sonnet 5를 사용합니다. 관측한 범위에서 프로젝트 `.claude/settings.json`에 `env`가 없고 `.claude/settings.local.json`의 env 키는 `CLAUDE_COST_CAP`뿐이어서 이 env 고정은 쓰이지 않습니다(사용자 scope는 미관측). 이 저장소의 에이전트 핀은 frontmatter의 Tier 2 전체 ID(R006)이며, auto mode classifier가 에이전트 핀과 무관한 모델로 동작한다는 점은 R006 모델 설명의 맥락 정보입니다.
|
|
1798
|
+
- CHANGELOG 원문: "Fixed Bash tool permission check to prompt before a `BASHPID` assignment whose value the shell would evaluate as arithmetic, instead of allowing it silently"
|
|
1799
|
+
(288) 셸이 산술식으로 평가할 값을 가진 `BASHPID` 할당을 조용히 허용하지 않고 먼저 확인합니다. R002 Bash 티어(Tier 4 승인) 방향의 플랫폼 수정이며 티어 정책은 바뀌지 않습니다. 저장소에 `BASHPID` 할당 사용은 관측되지 않았습니다.
|
|
1800
|
+
- CHANGELOG 원문: "Fixed a dangerous `rm` (such as one on `/` or the home directory) inside a `bash -c` or `sh -c` script running without a prompt in bypassPermissions mode or under a shell allow rule (anthropics/claude-code#96300)"
|
|
1801
|
+
(288) `bash -c`·`sh -c` 스크립트 안의 위험한 `rm`(예: `/`나 홈 디렉터리 대상)이 bypassPermissions 모드나 셸 allow 규칙 아래에서 프롬프트 없이 실행되던 결함이 수정되었습니다. 이 수정은 (287)의 `rm` 안전장치 수정이 스크립트 래퍼로 확장된 것이며, 가장 직접적인 선행 노트는 R001의 (261) 노트(positional parameter와 큰따옴표 `sh -c` 내부 `rm -rf`까지 확인을 확장)와 (273) 노트(bypass 모드 subshell 안의 `rm`)입니다. 관측한 범위에서 프로젝트 settings.json에는 `Bash(rm:*)` allow와 `defaultMode: bypassPermissions`(R010에 따라 2.1.257부터 프로젝트 scope에서는 무시됩니다)가 있고 `Bash(bash:*)`·`Bash(sh:*)` allow는 없으며, 유효 모드(사용자 scope)는 `auto`입니다. R001 문안 변경은 없고, 이 플랫폼 가드는 R001의 위임 전 blast-radius 열거를 대체하지 않습니다.
|
|
1802
|
+
- CHANGELOG 원문: "Changed the background command time limit to apply only in unattended sessions (`-p`, Agent SDK, CI, cloud); terminal, desktop app and VS Code sessions have no limit"
|
|
1803
|
+
(288) 백그라운드 명령 시간 제한이 무인 세션(`-p`, Agent SDK, CI, cloud)에만 적용되고, 터미널·데스크톱 앱·VS Code 세션에는 제한이 없습니다. 이 가이드 (285) 절의 백그라운드 시간 제한 항목("(288) 이후 이 제한은 좁혀졌습니다"로 이 불릿을 인용)과 이어지며, 룰 변경은 없습니다.
|
|
1804
|
+
|
|
1805
|
+
### 훅
|
|
1806
|
+
|
|
1807
|
+
- CHANGELOG 원문: "Fixed PreToolUse and PermissionRequest hooks being skipped when matching them failed or the tool's input could not be serialized to JSON; the call is now blocked"
|
|
1808
|
+
(288) matcher 평가 자체가 오류로 실패하거나 도구 입력을 JSON으로 직렬화할 수 없을 때 PreToolUse·PermissionRequest 훅이 건너뛰어지던 결함이 수정되어, 이제 해당 호출이 차단됩니다(matcher가 일치하지 않는 훅은 원래도 실행되지 않으며 이 항목의 대상이 아닙니다). 이 fail-closed 동작은 R021의 Hard Block 계열에 한정되지 않고 advisory를 포함한 모든 PreToolUse 그룹에 적용되므로, 평가 중 오류를 내는 matcher는 이제 호출을 차단합니다. 관측한 범위에서 PreToolUse 그룹은 12개(advisory 포함)이고 PermissionRequest 훅은 없습니다. R021 Advisory-first 정책 문안 변경은 없으며, 훅 matcher 편집 뒤 `bun run sync:hooks` 재생성 확인(R021)의 중요도가 커집니다.
|
|
1809
|
+
- CHANGELOG 원문: "Fixed `idle_prompt` notification hooks firing while background agents are still running (anthropics/claude-code#93672)"
|
|
1810
|
+
(288) 백그라운드 에이전트가 실행 중인데 `idle_prompt` 알림 훅이 발화하던 결함이 수정되었습니다. 관측한 범위에서 Notification 훅은 1개이고 matcher가 `*`이며 `.message`를 stderr에 출력할 뿐이어서 영향은 stderr 줄 1개 수준입니다. 이 훅이 `idle_prompt` 알림에도 걸리는지는 확인하지 않았습니다 [가설 — matcher *가 idle_prompt 알림에도 걸릴 가능성].
|
|
1811
|
+
- CHANGELOG 원문: "Fixed the InstructionsLoaded hook omitting agent_id and agent_type when a subagent's file access loads a rule or nested CLAUDE.md; rules and nested CLAUDE.md files loaded on file access now also report effort"
|
|
1812
|
+
(288) 서브에이전트의 파일 접근이 룰이나 nested CLAUDE.md를 로드할 때 InstructionsLoaded 훅이 agent_id·agent_type을 빠뜨리던 결함이 수정되었고, 파일 접근으로 로드된 룰·nested CLAUDE.md는 effort도 보고합니다. 관측한 범위에서 InstructionsLoaded 훅이 없어 영향이 없습니다(`jq -r '.hooks.InstructionsLoaded // [] | length' .claude/settings.json` = 0, `git grep -c -F 'InstructionsLoaded' -- .claude/hooks/hooks.json` 출력 없음(rc=1)). 훅 stdin 필드는 실측으로 확인한다는 기존 규율(R020 Config-Schema-Before-Edit)을 유지합니다.
|
|
1813
|
+
|
|
1814
|
+
### 지시 파일
|
|
1815
|
+
|
|
1816
|
+
- CHANGELOG 원문: "Fixed path-scoped `.claude/rules` and nested CLAUDE.md files not loading when Write or Edit creates or changes a file in their scope (previously only Read loaded them)"
|
|
1817
|
+
(288) path-scoped `.claude/rules`와 nested CLAUDE.md가 Write·Edit으로 해당 범위의 파일을 만들거나 바꿀 때도 로드됩니다(이전에는 Read만 로드). 관측한 범위에서 `.claude/rules/*.md`에는 frontmatter가 없어 path-scoped 룰이 없습니다(`paths:` 일치 1건은 R006 본문의 스킬 frontmatter 예시 줄입니다). `git ls-files '*CLAUDE.md'` 기준 CLAUDE.md 파일은 `CLAUDE.md`, `templates/CLAUDE.md` 2개이며, 이 중 nested CLAUDE.md는 `templates/CLAUDE.md`(루트와 내용이 다름)입니다. 따라서 288부터 `templates/` 아래 파일을 Write·Edit할 때도 이 파일이 로드됩니다. Edit과 기존 파일 덮어쓰기는 선행 Read가 필요하므로 새로 생기는 차이는 주로 새 파일 Write일 것입니다 [가설 — 선행 Read 때문에 Read 로드가 이미 일어났을 가능성]. R021의 `SessionStart` 재주입 경로는 별개이며 유지합니다.
|
|
1818
|
+
|
|
1819
|
+
### 서브에이전트 · Agent Teams
|
|
1820
|
+
|
|
1821
|
+
- CHANGELOG 원문: "Fixed agent teams: a plugin-defined agent spawned by name now runs with its own prompt, tools, disallowedTools and effort instead of the defaults"
|
|
1822
|
+
(288) agent teams에서 이름으로 스폰한 plugin 정의 에이전트가 기본값 대신 자기 prompt·tools·disallowedTools·effort로 실행됩니다. R018은 `TeamCreate` 부재 환경에서 비활성(dormant)이고 관측한 범위에서 `.claude-plugin/` 디렉토리가 없어 plugin 정의 에이전트도 없으므로 영향이 없습니다. 룰 변경은 없습니다.
|
|
1823
|
+
- CHANGELOG 원문: "Fixed a stall when launching an agent whose `tools:` lists very many `Agent(...)` entries"
|
|
1824
|
+
(288) `tools:`에 `Agent(...)` 항목이 매우 많은 에이전트를 시작할 때의 정체가 수정되었습니다. 관측한 범위에서 `.claude/agents/*.md` 중 `tools:`에 `Agent(` 항목이 있는 파일은 0개이므로 영향이 없습니다. R006 에이전트 설계 문안 변경은 없습니다.
|
|
1825
|
+
- CHANGELOG 원문: "Fixed Claude reporting a message to another session as delivered when that session held it: the notice now says it wasn't delivered and names the session, and in SDK sessions Claude can now learn of it mid-turn"
|
|
1826
|
+
(288) 다른 세션이 메시지를 보류하고 있는데 전달됨으로 보고하던 결함이 수정되어, 이제 전달되지 않았음과 세션 이름을 알리고 SDK 세션에서는 턴 도중에도 이를 알 수 있습니다. 직접 선행 노트는 R018의 (271) 노트(보류된 교차 세션 메시지, 전달됨 ≠ 읽힘)이며, R018 Scope의 "전달·열거 성공은 조율 신호일 뿐 승인 채널이 아니며 완료의 증거도 아니다"와 R020의 actual outcome ≠ attempt와 같은 방향입니다. 룰 변경은 없습니다.
|
|
1827
|
+
|
|
1828
|
+
### MCP · LSP
|
|
1829
|
+
|
|
1830
|
+
- CHANGELOG 원문: "Fixed MCP tool calls sometimes running twice when a remote server's result was over 16 MB or could not be parsed"
|
|
1831
|
+
(288) 원격 서버의 결과가 16MB를 넘거나 파싱할 수 없을 때 MCP 도구 호출이 간혹 두 번 실행되던 결함이 수정되었습니다. 부작용이 있는 MCP 호출의 중복 실행 위험에 관한 맥락 정보이며 R001·R002 문안 변경은 없습니다.
|
|
1832
|
+
- CHANGELOG 원문: "Fixed LSP tool calls hanging indefinitely when a language server uses dynamic capability registration or stops responding; requests now time out after 60s (per-server `requestTimeout`)"
|
|
1833
|
+
(288) language server가 동적 capability 등록을 쓰거나 응답을 멈출 때 LSP 도구 호출이 무한 대기하던 결함이 수정되어, 요청이 60초 뒤 타임아웃됩니다(서버별 `requestTimeout`). R002 Tier 3의 LSP 도구에 관한 맥락 정보이며 룰 변경은 없습니다.
|
|
1834
|
+
|
|
1835
|
+
### 텔레메트리
|
|
1836
|
+
|
|
1837
|
+
- CHANGELOG 원문: "Fixed OpenTelemetry `claude_code.tool.blocked_on_user` spans reporting `unknown` source or decision in `-p` and SDK sessions and for PreToolUse hook approvals"
|
|
1838
|
+
(288) `-p`·SDK 세션과 PreToolUse 훅 승인에서 OpenTelemetry `claude_code.tool.blocked_on_user` span이 source·decision을 `unknown`으로 보고하던 결함이 수정되었습니다. monitoring-setup 스킬을 다루는 맥락 정보입니다.
|
|
1839
|
+
- CHANGELOG 원문: "Fixed permission asks that ended unanswered, in `-p` or on an interrupted turn, emitting no `tool_decision` event"
|
|
1840
|
+
(288) `-p`에서 또는 인터럽트된 턴에서 응답 없이 끝난 권한 요청이 `tool_decision` 이벤트를 내보내지 않던 결함이 수정되었습니다. 응답 없는 권한 요청이 이제 관측 가능하다는 monitoring-setup 맥락 정보이며, R003 인터럽트 처리·R020 인터럽트 규칙의 문안 변경은 없습니다.
|
|
1841
|
+
|
|
1842
|
+
### 코드 리뷰
|
|
1843
|
+
|
|
1844
|
+
- CHANGELOG 원문: "Added `--max-findings <n>|all` to /code-review to report more or fewer findings than the usual limit; the choice is reused until you pass `--max-findings default`"
|
|
1845
|
+
(288) `/code-review`에 `--max-findings <n>|all`이 추가되어 보고 건수 상한을 바꿀 수 있고, 선택값은 `--max-findings default`를 지정할 때까지 다음 실행에도 유지됩니다. R023의 (232) 노트가 `/code-review`를 Tier 3 검증 호출로 다루므로, 상한이 걸린 리뷰를 완전한 리뷰로 오인할 수 있다는 함의가 있습니다. 관측한 범위에서 이 저장소 파이프라인에 `/code-review` 호출은 없습니다. 룰 변경은 없습니다.
|
|
1846
|
+
|
|
1847
|
+
### 설치
|
|
1848
|
+
|
|
1849
|
+
- CHANGELOG 원문: "Fixed the npm auto-updater reporting success when the platform-native binary failed to download and only the placeholder `claude` stub was installed"
|
|
1850
|
+
(288) npm auto-updater가 플랫폼 네이티브 바이너리 다운로드에 실패하고 placeholder `claude` 스텁만 설치됐는데도 성공을 보고하던 결함이 수정되었습니다. R020의 actual outcome ≠ attempt가 설치 도메인에 나타난 사례입니다. R017 게이트의 `claude --version` 실측이 스텁 설치 실패를 부수적으로 드러낼 수 있습니다 [가설 — 스텁이 `--version` 출력으로 구분되는지 미실측]. 룰 변경은 없습니다.
|
|
1851
|
+
|
|
1852
|
+
기타 59건 — 이 저장소 비해당(VSCode·Cloud sessions·Cowork·Claude Tag·Claude in Chrome·플러그인 마켓플레이스/`--plugin-dir`·mods·screen reader 모드·agents view 키바인딩·Windows·Bedrock/Vertex 인증 등 harness 비영향 세부사항). 실측: `gh release view v2.1.288 --repo anthropics/claude-code --json body --jq .body | grep -c '^- '` = 89건 중 위 본문 30건을 다뤘으므로 89 − 30 = 59건입니다. 계수 범위는 해당 릴리즈 노트의 최상위 불릿 줄(`- `로 시작하는 줄)입니다.
|
|
1853
|
+
|
|
1854
|
+
**Action items**:
|
|
1855
|
+
- 세션·재개·컨텍스트, 권한·auto mode·`rm`, 훅, 지시 파일, 서브에이전트·Agent Teams, MCP·LSP, 텔레메트리, 코드 리뷰, 설치 항목은 기존 룰이 이미 다루는 전제를 강화하거나, 이 저장소에 해당 구성이 없거나, 해당 구성이 있어도 현재 행동을 바꾸는 규범이 아니므로 룰 문안 변경은 불필요합니다. R021의 `SessionStart` 재주입은 유지합니다.
|
|
1856
|
+
- 제안 없음: R016 정책상 룰에는 현재 행동을 바꾸는 규범만 1줄로 들어가며, 위 항목 중 행동을 바꾸는 것은 확인되지 않았습니다.
|
|
1857
|
+
- 미확인 사항: Notification 훅(matcher `*`)이 `idle_prompt` 알림에도 걸리는지는 확인하지 않았습니다 [가설 — matcher *가 idle_prompt 알림에도 걸릴 가능성].
|
|
1858
|
+
- 미확인 사항: `infra/hada-scout/scout-runner.sh`(`timeout … claude -p`)의 실제 배포 여부와 이미지의 CC 버전은 확인하지 않았습니다 [가설 — 배포 이미지가 288 미만 CC를 쓸 가능성].
|
|
1859
|
+
|
|
1860
|
+
---
|
|
1861
|
+
|
|
1862
|
+
## v2.1.289 (2026-10-03)
|
|
1863
|
+
|
|
1864
|
+
> Issue: #1791 — Claude Code v2.1.289 compatibility documentation
|
|
1865
|
+
> Scope-ceiling check (R017): 조사 시점(2026-10-05)의 최신 릴리즈는 2.1.289입니다(`claude --version`=2.1.289, `npm view @anthropic-ai/claude-code version`=2.1.289). 따라서 이 릴리즈 이후 릴리즈의 롤백 확인 대상이 없습니다. 이 릴리즈 CHANGELOG의 "[VSCode] Reverted a 2.1.288 change to `claude auth status` that may have made sign-outs more frequent"는 2.1.288 항목의 롤백이며 VSCode 전용입니다(288 절에는 `claude auth status` 문구가 없습니다). 릴리즈 게시 시각은 2026-10-03T23:07:17Z입니다(`gh release view v2.1.289 --repo anthropics/claude-code --json publishedAt`).
|
|
1866
|
+
|
|
1867
|
+
### 권한 · Bash deny/ask 규칙
|
|
1868
|
+
|
|
1869
|
+
- CHANGELOG 원문: "Fixed Bash deny and ask rules missing a command behind an environment variable prefix with an expanded value (e.g. `TZ="$HOME" rm -rf build`) when the sandbox auto-allows commands"
|
|
1870
|
+
(289) sandbox가 명령을 자동 허용하는 경우, 확장된 값을 가진 환경 변수 접두(예: `TZ="$HOME" rm -rf build`) 뒤의 명령을 Bash deny·ask 규칙이 놓치던 결함이 수정되었습니다. R002·R001의 `rm` 금지 맥락과 같은 방향의 플랫폼 수정입니다. 관측한 범위에서 프로젝트 `.claude/settings.json`에는 `permissions.deny`가 0개이고 `sandbox` 설정이 없어 이 수정의 전제 조건(sandbox auto-allow)이 없으므로 영향이 없습니다.
|
|
1871
|
+
- CHANGELOG 원문: "Fixed a Bash deny or ask rule being skipped under sandbox auto-allow when a bare variable assignment came before the command"
|
|
1872
|
+
(289) sandbox auto-allow 상태에서 명령 앞에 bare 변수 할당이 오면 Bash deny·ask 규칙이 건너뛰어지던 결함이 수정되었습니다. 위 항목과 같은 전제(sandbox auto-allow)를 요구하므로 관측한 범위에서 이 저장소에는 해당하지 않습니다.
|
|
1873
|
+
- CHANGELOG 원문: "Fixed a deny or ask rule on a nested part of a compound shell command not holding over a user-installed mod's approval on managed machines"
|
|
1874
|
+
(289) 관리형 머신에서 compound shell 명령의 중첩 부분에 걸린 deny·ask 규칙이, 사용자가 설치한 mod의 승인 앞에서 유지되지 않던 결함이 수정되었습니다. 이 결함은 CHANGELOG 원문대로 관리형 머신에서만 발생하며, 관리형 머신 여부와 사용자가 설치한 mod는 머신·사용자 수준 구성이라 저장소에서 관측할 수 없습니다. 관측한 범위에서 이 저장소에는 mod 구성이 없고 R002 티어 정책도 바뀌지 않습니다. 이 개발 환경이 관리형 머신이 아니어서 영향이 없을 것으로 봅니다 [가설 — 머신 관리 여부는 미관측].
|
|
1875
|
+
- CHANGELOG 원문: "Fixed `Read` deny rules not applying to files @-mentioned, changed, or selected in the IDE through a symlink"
|
|
1876
|
+
(289) IDE에서 @-멘션되거나 변경되거나 선택된 파일이 심볼릭 링크를 통해 들어올 때 `Read` deny 규칙이 적용되지 않던 결함이 수정되었습니다. R002 파일 접근 범위와 같은 방향의 플랫폼 수정이며, 관측한 범위에서 프로젝트 `permissions.deny`가 0개이므로 이 저장소에서는 영향이 없습니다.
|
|
1877
|
+
|
|
1878
|
+
### MCP
|
|
1879
|
+
|
|
1880
|
+
- CHANGELOG 원문: "Fixed a user-installed plugin being able to rewrite the descriptions of an organization-managed MCP server's sign-in tools"
|
|
1881
|
+
(289) 사용자가 설치한 플러그인이 조직 관리 MCP 서버의 로그인 도구 설명을 다시 쓸 수 있던 결함이 수정되었습니다. 관측한 범위에서 이 저장소는 조직 관리 MCP 서버 구성을 두지 않으며 R002 Tier 6(MCP) 정책도 바뀌지 않습니다.
|
|
1882
|
+
|
|
1883
|
+
### 플러그인 · 에이전트
|
|
1884
|
+
|
|
1885
|
+
- CHANGELOG 원문: "Fixed `claude plugin validate` skipping the plugin when the folder also holds a marketplace manifest"
|
|
1886
|
+
(289) 폴더에 marketplace manifest도 함께 있으면 `claude plugin validate`가 플러그인을 건너뛰던 결함이 수정되었습니다. 관측한 범위에서 이 저장소의 검증 대상에는 marketplace manifest가 없으므로(`.claude-plugin/` 디렉토리 없음) 이 결함 조건에 해당하지 않습니다. 참고로 R017 본문에 "스킬 추가·수정 후 `claude plugin validate`를 개수 대조와 함께 실행한다(R023)"는 문장이 있으나, 이 수정은 그 문장의 동작을 바꾸지 않습니다.
|
|
1887
|
+
- CHANGELOG 원문: "Fixed `claude plugin validate` failing an Anthropic marketplace's own plugin and listing a clean `plugin.json` in `--json`"
|
|
1888
|
+
(289) `claude plugin validate`가 Anthropic marketplace의 자체 플러그인을 실패 처리하고 `--json` 출력에 깨끗한 `plugin.json`을 목록으로 나열하던 결함이 수정되었습니다. 결함 대상이 Anthropic marketplace의 자체 플러그인이고 이 저장소의 검증 대상에는 marketplace manifest가 없으므로 영향이 없습니다.
|
|
1889
|
+
- CHANGELOG 원문: "Added `agent.spawn` for teammates, one agent id across plugin hook events, and idle and waiting states in `$.agent.list()`"
|
|
1890
|
+
(289) teammates용 `agent.spawn`, 플러그인 훅 이벤트 전반의 단일 agent id, `$.agent.list()`의 idle·waiting 상태가 추가되었습니다. R010은 서브에이전트의 다른 서브에이전트 스폰을 프로젝트 정책으로 금지하며, 이 항목은 플러그인 API 표면이므로 R010을 바꾸지 않습니다. R018은 `TeamCreate` 부재 시 dormant이며 이 항목은 그 판정을 바꾸지 않습니다. 이 항목의 "plugin hook events"는 플러그인·mod 훅 표면이고, R021의 `r007-r008-drift-advisor.sh`가 읽는 것은 프로젝트 `settings.json` 훅의 stdin(`agent_id`)이므로 두 표면은 서로 다릅니다. 다만 플러그인 훅 agent id 변경이 프로젝트 훅 stdin의 `agent_id`에 영향을 줄 가능성은 남습니다 [가설 — 두 표면의 연동 여부는 미실측].
|
|
1891
|
+
|
|
1892
|
+
기타 19건 — 이 저장소 비해당(mods·plugin 패널 UI 렌더링, 터미널 렌더링·제어 문자 처리, `ui.render` 훅, 플러그인 표시 행, VSCode 전용 롤백 등 harness 비영향 세부사항이며 이 저장소는 mods·plugin 패널 UI를 개발하지 않습니다). 실측: `gh release view v2.1.289 --repo anthropics/claude-code --json body --jq .body | grep -c '^- '` = 27건 중 위 본문 8건을 다뤘으므로 27 − 8 = 19건입니다. 계수 범위는 해당 릴리즈 노트의 최상위 불릿 줄(`- `로 시작하는 줄)입니다.
|
|
1893
|
+
|
|
1894
|
+
**Action items**:
|
|
1895
|
+
- 권한·Bash deny/ask, MCP, 플러그인·에이전트 항목은 기존 룰이 이미 다루는 전제를 강화하거나 이 저장소에 해당 구성(sandbox, `permissions.deny`, 조직 관리 MCP, `.claude-plugin/`)이 없는 항목이며 룰 문안 변경은 불필요합니다.
|
|
1896
|
+
- 룰 변경 제안은 없습니다.
|
|
1897
|
+
|
|
1898
|
+
---
|
|
1899
|
+
|
|
1760
1900
|
## Known Platform Issues & Workarounds
|
|
1761
1901
|
|
|
1762
1902
|
### Agent tool malformed parsing on long / special-character prompts (#1241)
|
|
@@ -1781,6 +1921,7 @@ Opus 4.8에서 thinking blocks가 수정되어 API 오류가 발생하던 버그
|
|
|
1781
1921
|
- #1134 — Claude Code v2.1.140 release note
|
|
1782
1922
|
- #1137 — Claude Code v2.1.141 compatibility documentation
|
|
1783
1923
|
- #1158 — Claude Code v2.1.142 compatibility documentation
|
|
1924
|
+
- #1166 — CC v2.1.143 compatibility documentation
|
|
1784
1925
|
- #1147 — .gitignore nested .md pattern limitation note
|
|
1785
1926
|
- #1187 — Claude Code v2.1.144 compatibility documentation
|
|
1786
1927
|
- #1191 — Claude Code v2.1.145 compatibility documentation
|
|
@@ -1800,9 +1941,15 @@ Opus 4.8에서 thinking blocks가 수정되어 API 오류가 발생하던 버그
|
|
|
1800
1941
|
- #1714 — Claude Code v2.1.277 compatibility documentation
|
|
1801
1942
|
- #1716 — Claude Code v2.1.280 compatibility documentation
|
|
1802
1943
|
- #1717 — 컨텍스트 예산 초과 대응: CC 버전 노트 이관 정책 전환(룰 → 가이드)
|
|
1944
|
+
- #1731 — Claude Code v2.1.281 compatibility documentation
|
|
1803
1945
|
- #1746 — Claude Code v2.1.282 compatibility documentation
|
|
1804
1946
|
- #1747 — Claude Code v2.1.283 compatibility documentation
|
|
1805
1947
|
- #1755 — Claude Code v2.1.284 compatibility documentation
|
|
1948
|
+
- #1764 — Claude Code v2.1.285 compatibility documentation
|
|
1949
|
+
- #1765 — Claude Code v2.1.286 compatibility documentation
|
|
1950
|
+
- #1766 — Claude Code v2.1.287 compatibility documentation
|
|
1951
|
+
- #1790 — Claude Code v2.1.288 compatibility documentation
|
|
1952
|
+
- #1791 — Claude Code v2.1.289 compatibility documentation
|
|
1806
1953
|
- `.claude/skills/claude-native/` — auto-generation source
|
|
1807
1954
|
- `.claude/rules/SHOULD-hud-statusline.md` — R012 statusline integration
|
|
1808
1955
|
- `.claude/rules/MUST-agent-design.md` — R006 agent frontmatter spec
|
package/templates/manifest.json
CHANGED
|
@@ -171,8 +171,28 @@ steps:
|
|
|
171
171
|
- Otherwise: highest-priority open tier (bug → chore → feature)
|
|
172
172
|
2. Apply Step 1 filter (label-based exclusion and preference)
|
|
173
173
|
3. Sort by dependency: prerequisites before dependents
|
|
174
|
-
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers)
|
|
174
|
+
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers) —
|
|
175
|
+
EXCEPT the batch-size fill in rule 6, which takes precedence when the top tier has < 3.
|
|
175
176
|
5. Minimum 1 issue; if 0 eligible, halt with "no eligible issues for auto-dev run"
|
|
177
|
+
6. Batch-size norm (fill exception to "don't mix tiers"): if the top tier has fewer than
|
|
178
|
+
3 eligible issues, fill from the next tiers in order (bug → chore → feature, then
|
|
179
|
+
preferred-label / retrospective-proposal issues) until the scope is 3-7 issues. Tier
|
|
180
|
+
mixing is allowed ONLY for this fill. A 1-2 issue release is permitted ONLY when the
|
|
181
|
+
eligible total across ALL tiers is < 3. Issues split off or deferred pending approval
|
|
182
|
+
or a decision (`.claude/hooks/**` unattended split in Step 3, decision-needed, etc.)
|
|
183
|
+
do NOT count toward the eligible total. Because the Step 3 split happens AFTER this
|
|
184
|
+
step, re-evaluate this fill after the Step 3 split: if the remaining scope is below 3
|
|
185
|
+
and eligible issues remain, fill again from the next tiers, then re-run the Step 3 path
|
|
186
|
+
pre-check on any newly added issue. When the scope has multiple issues, issue
|
|
187
|
+
implementation delegations in parallel (R009) unless their target files overlap, or one
|
|
188
|
+
delegation depends on another's in-release implementation choice (see the implement
|
|
189
|
+
step's R009 「File-Disjoint ≠ Independent」 document-implementation variant); local git
|
|
190
|
+
state changes (per-issue lifecycle commits via mgr-gitnerd) remain serialized, one at a
|
|
191
|
+
time (R009 「File-Disjoint ≠ Independent (Local Git State)」).
|
|
192
|
+
Rationale: the fixed per-release cost (review rounds, CI, release.yml) must not be
|
|
193
|
+
carried by 1-2 issues; consecutive top-tier-only runs also starve lower tiers.
|
|
194
|
+
Origin: user feedback 2026-10-05 ("이슈를 너무 조금씩 해결한다"); #1814 harness proposal R3
|
|
195
|
+
(lower-tier starvation).
|
|
176
196
|
|
|
177
197
|
## Step 3 — Approval-required path pre-check (R010)
|
|
178
198
|
|
|
@@ -211,6 +231,9 @@ steps:
|
|
|
211
231
|
|
|
212
232
|
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
213
233
|
|
|
234
|
+
After an unattended split, re-evaluate the Step 2 rule 6 batch-size fill against the
|
|
235
|
+
remaining scope (split-off issues do not count) before assigning the milestone.
|
|
236
|
+
|
|
214
237
|
Assign scoped issues to milestone.
|
|
215
238
|
Output markdown release manifest:
|
|
216
239
|
| order | # | title | prerequisite | effort | labels |
|