mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-07-02 04:51:26 +08:00
fix: resolve issue cluster (#2295,#2298,#2303,#2304,#2305,#2306,#2340) + createdTime fallback bug
- session-manager: fix createdTime birthtime||ctime fallback that never fired (a Date is always truthy); use birthtimeMs>0 check via resolveCreatedTime() - installer: rewrite source-relative rules/skills links for the injected ecc/ namespace so installed skills resolve correctly (#2340) - continuous-learning-v2: drop unused mock import (#2305); standardize bash shebangs (#2303); poll for PID file instead of fixed sleep (#2295); rename _ecc_* -> _clv2_* (#2304); align promotion confidence docs (#2298); de-brittle Scope Decision Guide cross-reference (#2306) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,6 +5,29 @@ const path = require('path');
|
||||
|
||||
const { writeInstallState } = require('../install-state');
|
||||
const { filterMcpConfig, parseDisabledMcpServers } = require('../mcp-config');
|
||||
const { rewriteNamespaceLinks } = require('./rewrite-namespace-links');
|
||||
|
||||
const CLAUDE_ECC_NAMESPACE = 'ecc';
|
||||
|
||||
// Claude home/project installs inject `skills/ecc/` and `rules/ecc/`. Markdown
|
||||
// copied under those namespaced roots may carry source-relative links to a
|
||||
// sibling top-level dir that break post-install; rewrite them on copy.
|
||||
function getNamespaceLinkRewrite(plan, destinationPath) {
|
||||
if (!plan.adapter || (plan.adapter.target !== 'claude' && plan.adapter.target !== 'claude-project')) {
|
||||
return null;
|
||||
}
|
||||
if (!plan.targetRoot || !destinationPath.toLowerCase().endsWith('.md')) {
|
||||
return null;
|
||||
}
|
||||
const namespacedRoots = [
|
||||
path.join(plan.targetRoot, 'skills', CLAUDE_ECC_NAMESPACE) + path.sep,
|
||||
path.join(plan.targetRoot, 'rules', CLAUDE_ECC_NAMESPACE) + path.sep,
|
||||
];
|
||||
if (!namespacedRoots.some(root => destinationPath.startsWith(root))) {
|
||||
return null;
|
||||
}
|
||||
return CLAUDE_ECC_NAMESPACE;
|
||||
}
|
||||
|
||||
function readJsonObject(filePath, label) {
|
||||
let parsed;
|
||||
@@ -149,6 +172,16 @@ function applyInstallPlan(plan) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const namespace = operation.kind === 'copy-file'
|
||||
? getNamespaceLinkRewrite(plan, operation.destinationPath)
|
||||
: null;
|
||||
if (namespace) {
|
||||
const original = fs.readFileSync(operation.sourcePath, 'utf8');
|
||||
const rewritten = rewriteNamespaceLinks(original, namespace);
|
||||
fs.writeFileSync(operation.destinationPath, rewritten, 'utf8');
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.copyFileSync(operation.sourcePath, operation.destinationPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
// The Claude home/project installers inject a namespace segment into the
|
||||
// destination layout: `skills/` -> `skills/<ns>/` and `rules/` -> `rules/<ns>/`.
|
||||
// Skills that link to a sibling top-level dir using a source-relative path
|
||||
// (e.g. `../../rules/react/hooks.md`) break after install, because the extra
|
||||
// `<ns>/` level changes what `../../` resolves to and the target dir is itself
|
||||
// namespaced.
|
||||
//
|
||||
// This rewrites such links so they remain valid post-install: prepend one `../`
|
||||
// (to climb out of the injected namespace level) and insert the namespace
|
||||
// segment after the managed top-level dir name. Only links that climb with at
|
||||
// least two `../` and reference a namespaced managed dir (`rules`/`skills`) are
|
||||
// touched; intra-skill links (a single `../`) and absolute/bare paths are left
|
||||
// alone.
|
||||
|
||||
const NAMESPACED_DIRS = ['rules', 'skills'];
|
||||
|
||||
function buildLinkPattern() {
|
||||
const dirs = NAMESPACED_DIRS.join('|');
|
||||
// group 1: the `../` climb (>= 2), group 2: managed dir, group 3: trailing `/`
|
||||
return new RegExp(`((?:\\.\\./){2,})(${dirs})(/)`, 'g');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite source-relative links in a namespaced skill/rule markdown file so they
|
||||
* resolve correctly after the installer injects `<namespace>/`.
|
||||
* @param {string} content - file contents
|
||||
* @param {string} namespace - injected namespace segment (e.g. "ecc")
|
||||
* @returns {string} rewritten contents (unchanged if no matching links)
|
||||
*/
|
||||
function rewriteNamespaceLinks(content, namespace) {
|
||||
if (typeof content !== 'string' || !namespace) {
|
||||
return content;
|
||||
}
|
||||
|
||||
const pattern = buildLinkPattern();
|
||||
return content.replace(pattern, (match, climb, dir, slash, offset, fullText) => {
|
||||
// Already namespaced (`../../rules/ecc/...`) — leave untouched (idempotent).
|
||||
const rest = fullText.slice(offset + match.length);
|
||||
if (rest.startsWith(`${namespace}/`)) {
|
||||
return match;
|
||||
}
|
||||
return `../${climb}${dir}${slash}${namespace}/`;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { rewriteNamespaceLinks, NAMESPACED_DIRS };
|
||||
@@ -26,6 +26,18 @@ const {
|
||||
// "2026-02-01-ChezMoi_2-session.tmp"
|
||||
const SESSION_FILENAME_REGEX = /^(\d{4}-\d{2}-\d{2})(?:-([a-zA-Z0-9_][a-zA-Z0-9_-]*))?-session\.tmp$/;
|
||||
|
||||
/**
|
||||
* Resolve a file's creation time, preferring birthtime but falling back to
|
||||
* ctime when birthtime is unavailable. Some filesystems (e.g. overlayfs in
|
||||
* containers) report birthtime as epoch 0; a Date object is always truthy, so
|
||||
* `birthtime || ctime` would never fall back. Compare on milliseconds instead.
|
||||
* @param {import('fs').Stats} stats
|
||||
* @returns {Date}
|
||||
*/
|
||||
function resolveCreatedTime(stats) {
|
||||
return stats.birthtimeMs > 0 ? stats.birthtime : stats.ctime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse session filename to extract metadata
|
||||
* @param {string} filename - Session filename (e.g., "2026-01-17-abc123-session.tmp" or "2026-01-17-session.tmp")
|
||||
@@ -116,7 +128,7 @@ function getSessionCandidates(options = {}) {
|
||||
hasContent: stats.size > 0,
|
||||
size: stats.size,
|
||||
modifiedTime: stats.mtime,
|
||||
createdTime: stats.birthtime || stats.ctime
|
||||
createdTime: resolveCreatedTime(stats)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -151,7 +163,7 @@ function buildSessionRecord(sessionPath, metadata) {
|
||||
hasContent: stats.size > 0,
|
||||
size: stats.size,
|
||||
modifiedTime: stats.mtime,
|
||||
createdTime: stats.birthtime || stats.ctime
|
||||
createdTime: resolveCreatedTime(stats)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user