Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e6121d8
feat(dashboard): paginate change explorer
benym Aug 3, 2026
9ec7963
feat: activate projects from global defaults
benym Aug 3, 2026
b9043dd
merge: activate projects from global defaults
benym Aug 3, 2026
d8fb90e
fix: isolate Comet Hooks from user handlers
benym Aug 3, 2026
dafdef5
merge: integrate Hook isolation into Dashboard
benym Aug 3, 2026
b6239b0
fix: remove disabled ambient resume instructions
benym Aug 3, 2026
663ba9f
fix(dashboard): keep change explorer paginated on scroll
benym Aug 3, 2026
11283c3
fix(dashboard): constrain side panels to center height
benym Aug 3, 2026
213000d
fix(dashboard): hide redundant workspace scrollbars
benym Aug 3, 2026
2523814
fix(dashboard): bound Native workspace side panel
benym Aug 3, 2026
151628a
fix(dashboard): paginate Native change explorer
benym Aug 3, 2026
7b2bc79
fix(dashboard): align Classic workspace panel height
benym Aug 3, 2026
5c76c37
fix(dashboard): align Native workspace corner radius
benym Aug 3, 2026
632e2d7
test(dashboard): fill demo side panel content
benym Aug 3, 2026
c290fac
fix(dashboard): separate Native conflict count
benym Aug 3, 2026
f049839
fix(dashboard): match Native explorer tabs to Classic
benym Aug 3, 2026
8a5add8
fix(dashboard): align Native change explorer with Classic
benym Aug 3, 2026
f2e0a51
fix(dashboard): match Native change row visuals
benym Aug 3, 2026
27f52b2
fix(dashboard): align Native change badge with Classic
benym Aug 3, 2026
80a4730
fix(dashboard): align Native dark selection state
benym Aug 3, 2026
ac9c479
feat(dashboard): paginate Native changes
benym Aug 3, 2026
77f217e
fix(classic): require build plan before source writes
benym Aug 3, 2026
3518f62
fix(skill): restore Comet entry contract wording
benym Aug 3, 2026
4d7e7c8
build: refresh entry hook router
benym Aug 3, 2026
8f52d21
test: model managed Kiro hook removal failure
benym Aug 3, 2026
1a705a4
fix: address PR review findings
benym Aug 4, 2026
525ec74
fix(native): recover stale receipt scopes
benym Aug 4, 2026
89e3796
fix(entry): allow writes without active changes
benym Aug 4, 2026
25baac9
fix(skill): dispatch subagent workflows directly
benym Aug 4, 2026
b2636cb
fix(native): isolate parallel resume checks
benym Aug 4, 2026
a921e2d
fix(native): prevent incremental projection drift
benym Aug 4, 2026
1dd5336
merge: integrate Native projection fix into Dashboard
benym Aug 4, 2026
e7671dc
fix: address required PR review findings
benym Aug 4, 2026
2f9b563
test(classic): model occupied plan slots accurately
benym Aug 4, 2026
5b23f37
fix(native): avoid repeated Verify checks
benym Aug 4, 2026
9986521
merge: integrate master into dashboard-change-pagination
benym Aug 4, 2026
bcb7ccc
fix(dashboard): paginate changes without layout flicker
benym Aug 4, 2026
a39276f
feat(native): isolate changes before Shape
benym Aug 4, 2026
0b8d93c
fix(native): preserve isolated workspace invariants
benym Aug 4, 2026
22bcefa
fix(native): fail closed on invalid workspace state
benym Aug 4, 2026
1a2b118
fix(native): preserve workspace ownership on root move
benym Aug 4, 2026
c45d1c7
test(native): stabilize full-suite verification
benym Aug 4, 2026
64b9717
test: bound Git-heavy suite hooks
benym Aug 4, 2026
7728f4b
docs(native): archive workspace isolation change
benym Aug 4, 2026
6da7de2
chore: merge Native workspace isolation
benym Aug 4, 2026
519d947
fix(native): narrow workspace identity construction
benym Aug 4, 2026
6cc082e
test: bound full-suite execution time
benym Aug 4, 2026
2e75e2f
fix: isolate project hook lifecycle
benym Aug 5, 2026
0eb4ec6
docs: publish beta15 website link
benym Aug 5, 2026
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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to @rpamis/comet will be documented in this file.

## What's Changed [0.4.0-beta.15] - 2026-08-03

### Changed

- **Dashboard change explorer**: Dashboard now loads the current project overview with lightweight change rows, paginates active, archived, and all changes in batches of five, and fetches full artifact details only when selected so large projects remain responsive.
- **Global project activation**: A global initialization now stores complete Native or Classic defaults once, and the first explicit `/comet` invocation activates each unconfigured project from those defaults. Artifacts stay inside the project, activated projects keep stable settings when global defaults change, existing Classic projects retain Classic ownership, and project-scoped initialization remains optional for local asset copies or overrides.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- **Hook isolation**: Comet now installs its blocking Router only for project-scoped integrations and keeps it in a dedicated matcher group. Setup, update, and Doctor remove historical global Comet Hooks while preserving user-owned Hook entries and configuration.

### Fixed

- **Classic build recovery**: Full Classic workflows now return to plan creation after context recovery when no implementation plan is recorded, and block project source writes until the plan is restored and linked.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- **Ambient Resume cleanup**: Disabling the Ambient Resume probe now removes Comet-managed instructions from `AGENTS.md` and `CLAUDE.md` while preserving user-authored content.
- **External write compatibility**: Comet Hooks now remain neutral for unknown or project-external write targets, including paths redirected through symlinks or junctions, and evaluate only the in-project targets from mixed write operations so unrelated Hooks such as memory writers can continue independently.

## What's Changed [0.4.0-beta.14] - 2026-08-02

### Fixed
Expand Down
14 changes: 13 additions & 1 deletion README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ npm install -g @rpamis/comet

## 快速开始

一次全局初始化即可让已选宿主中的 `/comet` 在所有项目可用:

```bash
comet init --scope global
cd your-project
# 在宿主中调用 /comet
```

首次在一个尚未配置的项目中调用 `/comet` 时,Comet 会把 `~/.comet/config.yaml` 中的全局默认值固化为项目的 `.comet/config.yaml`,并只在该项目内创建产物目录。之后修改全局默认值不会改写已经激活的项目。Hook 继续识别这份项目配置与 `.comet/current-change.json`;仅触发 Hook、`comet status` 或普通只读解析不会自动初始化项目。

如需把 Skill、Rule 与 Hook 直接复制到项目,或显式覆盖该项目的工作流设置,仍可执行项目初始化:

```bash
cd your-project
comet init
Expand Down Expand Up @@ -204,7 +216,7 @@ Comet Eval的自动化双Agent架构能够在线上与LangSmith/LangFuse环境
| `--language <lang>` | 技能语言:`en` 或 `zh`(跳过交互式语言选择) |
| `--platform <platform>` | 只初始化指定平台;项目范围内可使用自定义平台 id |
| `--workflow <mode>` | 初始化工作流:`native`、`classic` 或 `both` |
| `--root <path>` | Native 的项目内产物根目录,例如 `docs` |
| `--root <path>` | Native 的项目内产物根目录;全局范围时保存为项目相对默认值 |
| `--codegraph <action>` | 非交互式项目索引操作:显式选择 `init` 或 `skip` |
| `--skip-existing` | 跳过已安装的组件 |
| `--overwrite` | 覆盖已安装的组件 |
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ npm install -g @rpamis/comet

## Quick Start

A single global initialization makes `/comet` available through the selected hosts in every project:

```bash
comet init --scope global
cd your-project
# Invoke /comet in the host
```

The first `/comet` invocation in an unconfigured project snapshots the defaults from `~/.comet/config.yaml` into the project's `.comet/config.yaml` and creates artifacts only inside that project. Later global-default changes do not rewrite activated projects. Hooks then recognize the project config and `.comet/current-change.json`; Hook events, `comet status`, and ordinary read-only resolution never initialize a project by themselves.

Run project initialization only when you want project-local copies of Skills, Rules, and Hooks, or explicit project overrides:

```bash
cd your-project
comet init
Expand Down Expand Up @@ -222,7 +234,7 @@ Initializes Comet for selected AI coding platforms. Interactive setup selects Na
| `--language <lang>` | Skill language: `en` or `zh` (skips interactive language prompt) |
| `--platform <platform>` | Initialize only this platform; project scope accepts custom platform ids |
| `--workflow <mode>` | Workflows to initialize: `native`, `classic`, or `both` |
| `--root <path>` | Project-relative Native artifact root, such as `docs` |
| `--root <path>` | Project-relative Native artifact root; global scope stores it as a project default |
| `--codegraph <action>` | Non-interactive project index action: explicitly choose `init` or `skip` |
| `--skip-existing` | Skip already installed components |
| `--overwrite` | Overwrite already installed components |
Expand Down
1 change: 1 addition & 0 deletions app/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const workflow = program.command('workflow').description('Resolve the configured
workflow
.command('resolve [path]')
.description('Resolve /comet to its permanent Native or Classic entry')
.option('--activate', 'Create project configuration from global defaults when missing')
.option('--json', 'Output as JSON')
.action(async (targetPath = '.', options) => {
const { workflowResolveCommand } = await import('../commands/workflow.js');
Expand Down
92 changes: 90 additions & 2 deletions app/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
getAssetsDir,
getManagedSkillPaths,
getManagedSkillPathsForSelection,
installCometHooksForPlatform,
} from '../../domains/skill/platform-install.js';
import { reconcileCometHooksForPlatform } from '../../domains/skill/hook-lifecycle.js';
import {
getPlatformRuleDestinations,
getLegacyPlatformRuleDestinations,
Expand Down Expand Up @@ -653,6 +653,24 @@ function getScopeBases(
return bases;
}

function globalHookCheckResult(
platform: Platform,
scope: InstallScope,
inspection: Awaited<ReturnType<typeof inspectCometHooksForPlatform>>,
): CheckResult {
const globalHookPresent =
inspection.present || inspection.managedPresent === true || inspection.legacyPresent === true;
return {
check: `hooks: ${platform.name} (${scope})`,
status: globalHookPresent || inspection.error ? 'warn' : 'pass',
message: inspection.error
? `${inspection.error} — run: comet doctor --repair --scope global`
: globalHookPresent
? 'global blocking Hook remains — run: comet doctor --repair --scope global'
: 'no global blocking Hook present',
};
}

async function checkPlatformComponents(
baseDir: string,
platform: (typeof PLATFORMS)[number],
Expand Down Expand Up @@ -740,6 +758,10 @@ async function checkPlatformComponents(
scope,
workflowSelection,
);
if (scope === 'global') {
results.push(globalHookCheckResult(platform, scope, inspection));
return results;
}
results.push({
check: `hooks: ${platform.name} (${scope})`,
status:
Expand Down Expand Up @@ -775,6 +797,36 @@ async function getPlatformsForSkillInspection(
}));
}

async function getGlobalHookOnlyInspections(
baseDir: string,
knownPlatformIds: ReadonlySet<string>,
): Promise<
Array<{
platform: Platform;
inspection: Awaited<ReturnType<typeof inspectCometHooksForPlatform>>;
}>
> {
const results: Array<{
platform: Platform;
inspection: Awaited<ReturnType<typeof inspectCometHooksForPlatform>>;
}> = [];
for (const platform of PLATFORMS) {
if (knownPlatformIds.has(platform.id) || !platform.supportsHooks || !platform.hookFormat) {
continue;
}
const inspection = await inspectCometHooksForPlatform(baseDir, platform, 'global');
if (
inspection.present ||
inspection.managedPresent ||
inspection.legacyPresent ||
inspection.error
) {
results.push({ platform, inspection });
}
}
return results;
}

async function checkSkillCompleteness(
projectPath: string,
scope: DoctorScope,
Expand All @@ -796,6 +848,7 @@ async function checkSkillCompleteness(
);
const total = managedSkills.length;
const platforms = await getPlatformsForSkillInspection(base.baseDir, base.scope, scope);
const detectedPlatformIds = new Set<string>();
for (const { platform, inspectComponents } of platforms) {
const skillsDirs = getPlatformSkillsDirs(platform, base.scope);
const canonicalSkillsDir = skillsDirs[0];
Expand All @@ -818,6 +871,7 @@ async function checkSkillCompleteness(
}

if (!detectedSkillsDir) continue;
detectedPlatformIds.add(platform.id);
anyCometInstall = true;
scopeState[base.scope].hasInstall = true;
const isLegacy = detectedSkillsDir !== canonicalSkillsDir;
Expand Down Expand Up @@ -855,6 +909,14 @@ async function checkSkillCompleteness(
);
}
}
if (base.scope === 'global') {
for (const { platform, inspection } of await getGlobalHookOnlyInspections(
base.baseDir,
detectedPlatformIds,
)) {
results.push(globalHookCheckResult(platform, base.scope, inspection));
}
}
}

if (scope === 'auto' && !scopeState.project.hasInstall && scopeState.global.hasComplete) {
Expand Down Expand Up @@ -1317,15 +1379,41 @@ async function repairDoctorState(
? 'native'
: 'classic';
const targets: Array<{ baseDir: string; scope: InstallScope; platform: Platform }> = [];
const hookOnlyTargets: Array<{ baseDir: string; scope: InstallScope; platform: Platform }> = [];

for (const base of getScopeBases(projectPath, scope, context)) {
const platforms = await getPlatformsForSkillInspection(base.baseDir, base.scope, scope);
const installedPlatformIds = new Set<string>();
for (const { platform, inspectComponents } of platforms) {
if (!inspectComponents || !(await hasManagedInstall(base.baseDir, platform, base.scope))) {
continue;
}
installedPlatformIds.add(platform.id);
targets.push({ baseDir: base.baseDir, scope: base.scope, platform });
}
if (base.scope === 'global') {
for (const { platform } of await getGlobalHookOnlyInspections(
base.baseDir,
installedPlatformIds,
)) {
hookOnlyTargets.push({ baseDir: base.baseDir, scope: base.scope, platform });
}
}
}

for (const target of hookOnlyTargets) {
const hookResult = await reconcileCometHooksForPlatform(
target.baseDir,
target.platform,
target.scope,
workflowSelection,
);
if (hookResult.status === 'failed') {
throw new Error(
`failed to repair Hook for ${target.platform.name} (${target.scope}): ${hookResult.reason}`,
);
}
repaired.push(`${target.platform.name} (${target.scope}) Hook`);
}

for (const target of targets) {
Expand All @@ -1334,7 +1422,7 @@ async function repairDoctorState(
const runtime = hookRouterRuntimePaths(baseDir, platform, targetScope);
await copyFile(runtime.source, runtime.destination);
}
const hookResult = await installCometHooksForPlatform(
const hookResult = await reconcileCometHooksForPlatform(
baseDir,
platform,
targetScope,
Expand Down
68 changes: 55 additions & 13 deletions app/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ import type { InstallMode } from '../../platform/install/types.js';
import {
copyCometSkillsForPlatform,
copyCometRulesForPlatform,
installCometHooksForPlatform,
createWorkingDirs,
mergeProjectConfig,
prepareNativeSkillInstallTarget,
} from '../../domains/skill/platform-install.js';
import { installCometProjectInstructions } from '../../domains/skill/project-instructions.js';
import { reconcileCometHooksForPlatform } from '../../domains/skill/hook-lifecycle.js';
import { syncCometProjectInstructions } from '../../domains/skill/project-instructions.js';
import { LANGUAGES, type LanguageConfig } from '../../domains/skill/languages.js';
import { resolveInitWorkflow } from '../../domains/comet-entry/init-workflow.js';
import type { CometWorkflow, InitWorkflowSelection } from '../../domains/comet-entry/types.js';
Expand Down Expand Up @@ -59,7 +58,11 @@ import {
readWorkflowProjectConfigSnapshot,
} from '../../domains/workflow-contract/project-config-reader.js';
import { writeWorkflowProjectConfig } from '../../domains/workflow-contract/project-config-writer.js';
import type { WorkflowProjectConfig } from '../../domains/workflow-contract/types.js';
import { writeWorkflowGlobalConfig } from '../../domains/workflow-contract/global-config.js';
import type {
WorkflowGlobalConfig,
WorkflowProjectConfig,
} from '../../domains/workflow-contract/types.js';
import { installSuperpowersForPlatforms } from '../../domains/integrations/superpowers.js';
import {
hasCodegraphProjectIndex,
Expand Down Expand Up @@ -502,9 +505,6 @@ export async function initCommand(

const detected = await detectPlatforms(projectPath);
const scope = await selectScope(options, lang);
if (scope === 'global' && options.artifactRoot !== undefined) {
throw new Error('--root is only valid for project-scope initialization');
}
if (scope === 'global' && options.codegraph === 'init') {
throw new Error('--codegraph init is only valid for project-scope initialization');
}
Expand Down Expand Up @@ -535,8 +535,15 @@ export async function initCommand(
configuredWorkflows.includes('native') &&
configuredWorkflows.includes('classic')
? 'both'
: (suggestedWorkflowDecision?.workflow ?? 'classic');
: (suggestedWorkflowDecision?.workflow ?? 'native');
const workflowSelection = await selectWorkflow(options, lang, suggestedWorkflowSelection);
if (
scope === 'global' &&
options.artifactRoot !== undefined &&
!includesWorkflow(workflowSelection, 'native')
) {
throw new Error('--root is only valid when the Native workflow is enabled');
}
const workflow: CometWorkflow = workflowSelection === 'both' ? 'native' : workflowSelection;
const workflowDecision =
scope === 'project'
Expand Down Expand Up @@ -918,7 +925,7 @@ export async function initCommand(
status,
reason,
cleanupFailed = 0,
} = await installCometHooksForPlatform(baseDir, platform, scope, workflowSelection);
} = await reconcileCometHooksForPlatform(baseDir, platform, scope, workflowSelection);
cometComponentInstalled ||= status === 'installed';
if (status === 'installed') {
if (scope === 'project') projectRouterInstalled = true;
Expand Down Expand Up @@ -1068,6 +1075,10 @@ export async function initCommand(
const cometInstallComplete =
results.length > 0 && results.every((result) => result.comet !== 'failed');
const projectInitializationComplete = cometInstallComplete && classicOpenSpecRootReady;
const globalWorkflowConfigReady =
scope !== 'global' ||
!includesWorkflow(workflowSelection, 'classic') ||
osGlobalStatus === 'installed';

if (
scope === 'project' &&
Expand Down Expand Up @@ -1097,8 +1108,15 @@ export async function initCommand(
}
workingDirsCreated = true;

if (includesWorkflow(workflowSelection, 'native')) {
await installCometProjectInstructions(projectPath, language.id);
if (
includesWorkflow(workflowSelection, 'native') ||
initialProjectConfigDocument?.ambient_resume === false
) {
await syncCometProjectInstructions(
projectPath,
language.id,
initialProjectConfigDocument?.ambient_resume ?? true,
);
}

const successfulCometPlatforms = new Set(
Expand Down Expand Up @@ -1215,8 +1233,32 @@ export async function initCommand(
projectConfigCreated = initialProjectConfigDocument === null;
projectConfigUpdated = initialProjectConfigDocument !== null;
}
} else if (scope === 'global') {
await mergeProjectConfig(baseDir, language.artifactLanguage);
} else if (scope === 'global' && globalWorkflowConfigReady) {
const defaults = defaultProjectConfig(
options.artifactRoot ?? 'docs',
language.artifactLanguage,
);
const selectedWorkflows =
workflowSelection === 'both' ? (['native', 'classic'] as const) : [workflowSelection];
const config: WorkflowGlobalConfig = {
schema: 'comet.global.v1',
default_workflow: workflow,
workflows: [...selectedWorkflows],
ambient_resume: true,
...(includesWorkflow(workflowSelection, 'native') ? { native: defaults.native } : {}),
...(includesWorkflow(workflowSelection, 'classic')
? {
classic: {
artifact_layout: 'docs',
language: language.artifactLanguage,
context_compression: 'off',
review_mode: 'standard',
auto_transition: true,
},
}
: {}),
};
await writeWorkflowGlobalConfig(baseDir, config);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
} catch (error) {
finalizationFailure = (error as Error).message;
Expand Down
Loading
Loading