aiwcli 0.10.2 → 0.11.0
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/bin/run.js +1 -1
- package/dist/commands/clear.d.ts +11 -6
- package/dist/commands/clear.js +229 -381
- package/dist/commands/init/index.d.ts +1 -17
- package/dist/commands/init/index.js +22 -107
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/lib/template-installer.d.ts +7 -12
- package/dist/lib/template-installer.js +69 -193
- package/dist/lib/template-settings-reconstructor.d.ts +35 -0
- package/dist/lib/template-settings-reconstructor.js +130 -0
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
- package/dist/templates/_shared/lib-ts/base/constants.ts +306 -0
- package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +439 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +252 -0
- package/dist/templates/_shared/lib-ts/base/logger.ts +250 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +116 -0
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +162 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +438 -0
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +515 -0
- package/dist/templates/_shared/lib-ts/context/context-store.ts +707 -0
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +316 -0
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -0
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +216 -0
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/package.json +21 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +104 -0
- package/dist/templates/_shared/{lib/templates/plan_context.py → lib-ts/templates/plan-context.ts} +14 -22
- package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
- package/dist/templates/_shared/lib-ts/types.ts +164 -0
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/ARCH-EVOLUTION.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-PATTERNS.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-STRUCTURE.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-CHAIN-TRACER.md → ASSUMPTION-TRACER.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLARITY-AUDITOR.md +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +74 -3
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-FEASIBILITY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-GAPS.md +71 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-ORDERING.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/CONSTRAINT-VALIDATOR.md +73 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-ADR-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-SCALE-MATCHER.md +65 -0
- package/dist/templates/cc-native/_cc-native/agents/DEVILS-ADVOCATE.md +6 -9
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-PHILOSOPHY.md +87 -0
- package/dist/templates/cc-native/_cc-native/agents/HANDOFF-READINESS.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY-DETECTOR.md → HIDDEN-COMPLEXITY.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/INCREMENTAL-DELIVERY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +91 -18
- package/dist/templates/cc-native/_cc-native/agents/RISK-DEPENDENCY.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-FMEA.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-PREMORTEM.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-REVERSIBILITY.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/SCOPE-BOUNDARY.md +78 -0
- package/dist/templates/cc-native/_cc-native/agents/SIMPLICITY-GUARDIAN.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/SKEPTIC.md +16 -12
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-BEHAVIOR-AUDITOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-CHARACTERIZATION.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-FIRST-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-PYRAMID-ANALYZER.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-COSTS.md +68 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-STAKEHOLDERS.md +66 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-COVERAGE.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-STRENGTH.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +12 -16
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/lib/template-merger.d.ts +0 -47
- package/dist/lib/template-merger.js +0 -162
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -169
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -341
- package/dist/templates/_shared/lib/base/inference.py +0 -318
- package/dist/templates/_shared/lib/base/logger.py +0 -291
- package/dist/templates/_shared/lib/base/stop_words.py +0 -213
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -242
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -204
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -701
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/agents/ACCESSIBILITY-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/ARCHITECT-REVIEWER.md +0 -48
- package/dist/templates/cc-native/_cc-native/agents/CODE-REVIEWER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-CHECKER.md +0 -59
- package/dist/templates/cc-native/_cc-native/agents/CONTEXT-EXTRACTOR.md +0 -92
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-REVIEWER.md +0 -51
- package/dist/templates/cc-native/_cc-native/agents/FEASIBILITY-ANALYST.md +0 -57
- package/dist/templates/cc-native/_cc-native/agents/FRESH-PERSPECTIVE.md +0 -54
- package/dist/templates/cc-native/_cc-native/agents/INCENTIVE-MAPPER.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/PENETRATION-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/PERFORMANCE-ENGINEER.md +0 -75
- package/dist/templates/cc-native/_cc-native/agents/PRECEDENT-FINDER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/REVERSIBILITY-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/RISK-ASSESSOR.md +0 -58
- package/dist/templates/cc-native/_cc-native/agents/SECOND-ORDER-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/STAKEHOLDER-ADVOCATE.md +0 -55
- package/dist/templates/cc-native/_cc-native/agents/TRADE-OFF-ILLUMINATOR.md +0 -204
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -869
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1027
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -2,98 +2,6 @@ import { promises as fs } from 'node:fs';
|
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { IdePathResolver } from './ide-path-resolver.js';
|
|
4
4
|
import { pathExists } from './paths.js';
|
|
5
|
-
import { mergeTemplateContent } from './template-merger.js';
|
|
6
|
-
/**
|
|
7
|
-
* Deep merge two settings objects, combining hook arrays.
|
|
8
|
-
* Used to merge _shared/settings.json into .claude/settings.json
|
|
9
|
-
*/
|
|
10
|
-
function deepMergeSettings(target, source) {
|
|
11
|
-
const result = { ...target };
|
|
12
|
-
for (const key of Object.keys(source)) {
|
|
13
|
-
// Skip comment fields
|
|
14
|
-
if (key.startsWith('$') || key.startsWith('_')) {
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
const sourceValue = source[key];
|
|
18
|
-
const targetValue = result[key];
|
|
19
|
-
if (key === 'hooks' && typeof sourceValue === 'object' && sourceValue !== null) {
|
|
20
|
-
// Special handling for hooks - merge by event type
|
|
21
|
-
result[key] = mergeHooks(targetValue || {}, sourceValue);
|
|
22
|
-
}
|
|
23
|
-
else if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
|
|
24
|
-
// Concatenate arrays
|
|
25
|
-
result[key] = [...targetValue, ...sourceValue];
|
|
26
|
-
}
|
|
27
|
-
else if (typeof sourceValue === 'object' && sourceValue !== null && typeof targetValue === 'object' && targetValue !== null) {
|
|
28
|
-
// Recursively merge objects
|
|
29
|
-
result[key] = deepMergeSettings(targetValue, sourceValue);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
// Override with source value
|
|
33
|
-
result[key] = sourceValue;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Merge hook configurations, combining arrays for each event type.
|
|
40
|
-
*/
|
|
41
|
-
function mergeHooks(target, source) {
|
|
42
|
-
const result = { ...target };
|
|
43
|
-
for (const eventType of Object.keys(source)) {
|
|
44
|
-
const targetHooks = result[eventType];
|
|
45
|
-
const sourceHooks = source[eventType];
|
|
46
|
-
if (targetHooks && sourceHooks) {
|
|
47
|
-
// Append source hooks to existing event type
|
|
48
|
-
result[eventType] = [...targetHooks, ...sourceHooks];
|
|
49
|
-
}
|
|
50
|
-
else if (sourceHooks) {
|
|
51
|
-
// New event type
|
|
52
|
-
result[eventType] = sourceHooks;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Merge settings from a source settings.json file into the IDE settings file.
|
|
59
|
-
* Reads from the provided source path and merges into .claude/settings.json at project root.
|
|
60
|
-
*
|
|
61
|
-
* @param targetDir - Project root directory
|
|
62
|
-
* @param sourceSettingsPath - Absolute path to source settings.json file
|
|
63
|
-
* @returns true if merge successful, false otherwise
|
|
64
|
-
*/
|
|
65
|
-
async function mergeSharedSettingsFromSource(targetDir, sourceSettingsPath) {
|
|
66
|
-
const resolver = new IdePathResolver(targetDir);
|
|
67
|
-
const ideSettingsPath = resolver.getClaudeSettings();
|
|
68
|
-
// Check if source settings exists
|
|
69
|
-
if (!(await pathExists(sourceSettingsPath))) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
// Read source settings
|
|
74
|
-
const sourceContent = await fs.readFile(sourceSettingsPath, 'utf8');
|
|
75
|
-
const sourceSettings = JSON.parse(sourceContent);
|
|
76
|
-
// Read IDE settings (create empty object if doesn't exist)
|
|
77
|
-
let ideSettings = {};
|
|
78
|
-
if (await pathExists(ideSettingsPath)) {
|
|
79
|
-
const ideContent = await fs.readFile(ideSettingsPath, 'utf8');
|
|
80
|
-
ideSettings = JSON.parse(ideContent);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
// Create .claude directory if it doesn't exist
|
|
84
|
-
await fs.mkdir(dirname(ideSettingsPath), { recursive: true });
|
|
85
|
-
}
|
|
86
|
-
// Merge source settings into IDE settings
|
|
87
|
-
const mergedSettings = deepMergeSettings(ideSettings, sourceSettings);
|
|
88
|
-
// Write merged settings back
|
|
89
|
-
await fs.writeFile(ideSettingsPath, JSON.stringify(mergedSettings, null, 4) + '\n', 'utf8');
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
// Silently fail on parse/write errors
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
5
|
/**
|
|
98
6
|
* Check template installation status for a method.
|
|
99
7
|
* Returns which items exist and which are missing.
|
|
@@ -186,32 +94,6 @@ export function shouldExclude(name) {
|
|
|
186
94
|
return pattern.test(name);
|
|
187
95
|
});
|
|
188
96
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Merge source directory into destination, skipping existing files.
|
|
191
|
-
* Unlike copyDir, this preserves existing files in destination.
|
|
192
|
-
*
|
|
193
|
-
* @param src - Source directory path
|
|
194
|
-
* @param dest - Destination directory path
|
|
195
|
-
*/
|
|
196
|
-
async function mergeDirectory(src, dest) {
|
|
197
|
-
await fs.mkdir(dest, { recursive: true });
|
|
198
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
199
|
-
for (const entry of entries) {
|
|
200
|
-
if (shouldExclude(entry.name))
|
|
201
|
-
continue;
|
|
202
|
-
const srcPath = join(src, entry.name);
|
|
203
|
-
const destPath = join(dest, entry.name);
|
|
204
|
-
if (entry.isDirectory()) {
|
|
205
|
-
await mergeDirectory(srcPath, destPath);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
// Skip if file already exists
|
|
209
|
-
if (!(await pathExists(destPath))) {
|
|
210
|
-
await fs.copyFile(srcPath, destPath);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
97
|
/**
|
|
216
98
|
* Copy directory recursively with proper error handling.
|
|
217
99
|
* Excludes test files, cache directories, and output folders.
|
|
@@ -249,20 +131,45 @@ export async function copyDir(src, dest, excludeIdeFolders = false) {
|
|
|
249
131
|
});
|
|
250
132
|
await Promise.all(operations);
|
|
251
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Merge source directory into destination, skipping existing files.
|
|
136
|
+
* Unlike copyDir, this preserves existing files in destination.
|
|
137
|
+
*
|
|
138
|
+
* @param src - Source directory path
|
|
139
|
+
* @param dest - Destination directory path
|
|
140
|
+
*/
|
|
141
|
+
async function mergeDirectory(src, dest) {
|
|
142
|
+
await fs.mkdir(dest, { recursive: true });
|
|
143
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
144
|
+
const ops = entries
|
|
145
|
+
.filter((entry) => !shouldExclude(entry.name))
|
|
146
|
+
.map(async (entry) => {
|
|
147
|
+
const srcPath = join(src, entry.name);
|
|
148
|
+
const destPath = join(dest, entry.name);
|
|
149
|
+
if (entry.isDirectory()) {
|
|
150
|
+
await mergeDirectory(srcPath, destPath);
|
|
151
|
+
}
|
|
152
|
+
else if (!(await pathExists(destPath))) {
|
|
153
|
+
await fs.copyFile(srcPath, destPath);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
await Promise.all(ops);
|
|
157
|
+
}
|
|
252
158
|
/**
|
|
253
159
|
* Install template with IDE-specific folder selection.
|
|
254
|
-
* Supports selective installation - only installs items that don't already exist.
|
|
255
160
|
*
|
|
256
161
|
* Template structure:
|
|
257
|
-
* - Non-dot folders (e.g., _bmad/, GSR/)
|
|
258
|
-
* -
|
|
162
|
+
* - Non-dot folders (e.g., _bmad/, GSR/) → .aiwcli/ (always overwritten)
|
|
163
|
+
* - _shared/ → .aiwcli/_shared/ (always overwritten)
|
|
164
|
+
* - IDE dot folders (e.g., .claude/) → decomposed into method-owned subdirs
|
|
165
|
+
*
|
|
166
|
+
* Settings reconstruction is handled separately by the caller via reconstructIdeSettings().
|
|
259
167
|
*
|
|
260
168
|
* @param config - Installation configuration
|
|
261
|
-
* @
|
|
262
|
-
* @returns Installation result with list of installed and skipped folders
|
|
169
|
+
* @returns Installation result with list of installed folders
|
|
263
170
|
* @throws Error if template doesn't exist or requested IDE folder not found
|
|
264
171
|
*/
|
|
265
|
-
export async function installTemplate(config
|
|
172
|
+
export async function installTemplate(config) {
|
|
266
173
|
const { templateName, targetDir, ides, templatePath } = config;
|
|
267
174
|
// Verify template exists
|
|
268
175
|
try {
|
|
@@ -295,108 +202,77 @@ export async function installTemplate(config, skipExisting = true) {
|
|
|
295
202
|
`Available: ${availableIdes.join(', ')}`);
|
|
296
203
|
}
|
|
297
204
|
const installedFolders = [];
|
|
298
|
-
const skippedFolders = [];
|
|
299
|
-
const mergedFolders = [];
|
|
300
|
-
let mergedFileCount = 0;
|
|
301
205
|
// Create .aiwcli container folder for method-specific files
|
|
302
206
|
const resolver = new IdePathResolver(targetDir);
|
|
303
207
|
const containerDir = resolver.getAiwcliContainer();
|
|
304
208
|
await fs.mkdir(containerDir, { recursive: true });
|
|
305
|
-
// Install non-dot folders into .aiwcli/ container (
|
|
209
|
+
// Install non-dot folders into .aiwcli/ container (always overwrite)
|
|
306
210
|
const nonDotInstalls = nonDotFolders.map(async (folder) => {
|
|
307
211
|
const srcPath = join(templatePath, folder);
|
|
308
|
-
// Destination is inside .aiwcli/ container
|
|
309
212
|
const destPath = join(containerDir, folder);
|
|
310
|
-
if (skipExisting && (await pathExists(destPath))) {
|
|
311
|
-
return { folder, skipped: true };
|
|
312
|
-
}
|
|
313
213
|
await copyDir(srcPath, destPath);
|
|
314
|
-
return
|
|
214
|
+
return folder;
|
|
315
215
|
});
|
|
316
216
|
const nonDotResults = await Promise.all(nonDotInstalls);
|
|
317
|
-
|
|
318
|
-
if (result.skipped) {
|
|
319
|
-
skippedFolders.push(result.folder);
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
installedFolders.push(result.folder);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
217
|
+
installedFolders.push(...nonDotResults);
|
|
325
218
|
// Install root-level _shared directory (shared across all templates)
|
|
326
|
-
// This is at templates/_shared, not inside the specific template directory
|
|
327
219
|
// Exclude IDE config folders (.claude, .windsurf) - they are used for settings merging only
|
|
328
220
|
const templatesRoot = dirname(templatePath);
|
|
329
221
|
const rootSharedSrc = join(templatesRoot, '_shared');
|
|
330
222
|
const rootSharedDest = join(containerDir, '_shared');
|
|
331
223
|
if (await pathExists(rootSharedSrc)) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
await copyDir(rootSharedSrc, rootSharedDest, true); // excludeIdeFolders = true
|
|
337
|
-
installedFolders.push('_shared');
|
|
338
|
-
}
|
|
339
|
-
// Merge shared IDE folders (commands, workflows) into project IDE folders
|
|
340
|
-
// This handles _shared/.claude/, _shared/.windsurf/, etc.
|
|
224
|
+
await copyDir(rootSharedSrc, rootSharedDest, true); // excludeIdeFolders = true
|
|
225
|
+
installedFolders.push('_shared');
|
|
226
|
+
// Copy shared IDE content (e.g., _shared/.claude/commands/handoff.md)
|
|
227
|
+
// These are non-method-owned files that live in IDE folders
|
|
341
228
|
const sharedIdeInstalls = ides.map(async (ide) => {
|
|
342
229
|
const sharedIdeFolder = join(rootSharedSrc, `.${ide}`);
|
|
343
230
|
if (await pathExists(sharedIdeFolder)) {
|
|
344
231
|
const destIdeFolder = resolver.getIdeDir(ide);
|
|
345
232
|
await fs.mkdir(destIdeFolder, { recursive: true });
|
|
346
|
-
//
|
|
233
|
+
// Merge shared IDE content, skipping files that already exist
|
|
347
234
|
await mergeDirectory(sharedIdeFolder, destIdeFolder);
|
|
348
235
|
}
|
|
349
236
|
});
|
|
350
237
|
await Promise.all(sharedIdeInstalls);
|
|
351
238
|
}
|
|
352
|
-
// Install
|
|
353
|
-
//
|
|
239
|
+
// Install method-owned IDE content (decomposed approach)
|
|
240
|
+
// Instead of copying entire .claude/ from template, only copy method-namespaced subdirectories
|
|
354
241
|
const ideInstalls = ides.map(async (ide) => {
|
|
355
242
|
const folderName = dotFolders.get(ide);
|
|
356
|
-
if (folderName)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
243
|
+
if (!folderName)
|
|
244
|
+
return null;
|
|
245
|
+
const srcIdePath = join(templatePath, folderName);
|
|
246
|
+
const destIdePath = resolver.getIdeDir(ide);
|
|
247
|
+
await fs.mkdir(destIdePath, { recursive: true });
|
|
248
|
+
// Scan the template IDE folder for subdirectories and copy method-owned content
|
|
249
|
+
const ideEntries = await fs.readdir(srcIdePath, { withFileTypes: true });
|
|
250
|
+
const subdirOps = ideEntries
|
|
251
|
+
.filter((entry) => entry.isDirectory())
|
|
252
|
+
.map(async (entry) => {
|
|
253
|
+
const subdirSrc = join(srcIdePath, entry.name);
|
|
254
|
+
const subdirDest = join(destIdePath, entry.name);
|
|
255
|
+
// Check for method-namespaced child within this subdirectory
|
|
256
|
+
const methodChildSrc = join(subdirSrc, templateName);
|
|
257
|
+
if (await pathExists(methodChildSrc)) {
|
|
258
|
+
// Copy only the method-namespaced subdirectory (overwrite)
|
|
259
|
+
const methodChildDest = join(subdirDest, templateName);
|
|
260
|
+
await copyDir(methodChildSrc, methodChildDest);
|
|
373
261
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
262
|
+
else {
|
|
263
|
+
// No method-namespaced child — copy the entire subdirectory, merging with existing
|
|
264
|
+
await mergeDirectory(subdirSrc, subdirDest);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
await Promise.all(subdirOps);
|
|
268
|
+
return folderName;
|
|
378
269
|
});
|
|
379
270
|
const ideResults = (await Promise.all(ideInstalls)).filter((result) => result !== null);
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
mergedFolders.push(result.folder);
|
|
383
|
-
mergedFileCount += result.mergedFiles;
|
|
384
|
-
}
|
|
385
|
-
else if (result.skipped) {
|
|
386
|
-
skippedFolders.push(result.folder);
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
installedFolders.push(result.folder);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
// Settings merging is now handled by the caller via mergeMethodsSettings()
|
|
393
|
-
// This allows unified merging of _shared + method-specific settings
|
|
271
|
+
installedFolders.push(...ideResults);
|
|
272
|
+
// Settings reconstruction is handled by the caller via reconstructIdeSettings()
|
|
394
273
|
return {
|
|
395
274
|
installedFolders,
|
|
396
|
-
|
|
397
|
-
mergedFolders,
|
|
398
|
-
mergedFileCount,
|
|
399
|
-
sharedSettingsMerged: false, // Deprecated, kept for backwards compatibility
|
|
275
|
+
sharedSettingsMerged: false,
|
|
400
276
|
templatePath,
|
|
401
277
|
};
|
|
402
278
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified reconstruction of shared IDE settings files.
|
|
3
|
+
*
|
|
4
|
+
* Both `aiw init` and `aiw clear` use the same operation: reconstruct shared
|
|
5
|
+
* settings from the union of all active templates. Install adds a template to
|
|
6
|
+
* the list; clear removes one. The reconstruction logic is identical.
|
|
7
|
+
*
|
|
8
|
+
* Template files fall into two categories:
|
|
9
|
+
* 1. **Method-owned** — lives in method-namespaced paths (e.g., `.claude/commands/cc-native/`),
|
|
10
|
+
* owned exclusively by one template. Handled by copy/delete, not by this module.
|
|
11
|
+
* 2. **Shared** — lives in common locations (e.g., `settings.json`), multiple templates
|
|
12
|
+
* contribute. Handled by this module via reconstruction from source.
|
|
13
|
+
*
|
|
14
|
+
* @module lib/template-settings-reconstructor
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Reconstruct .claude/settings.json and .windsurf/hooks.json from the union
|
|
18
|
+
* of all specified templates.
|
|
19
|
+
*
|
|
20
|
+
* The function:
|
|
21
|
+
* 1. Starts with empty settings
|
|
22
|
+
* 2. Merges _shared template settings (always included)
|
|
23
|
+
* 3. For each active template, merges its template-source settings
|
|
24
|
+
* 4. Writes the result to the IDE settings file
|
|
25
|
+
*
|
|
26
|
+
* Uses mergeClaudeSettings() from hooks-merger.ts for dedup-aware merging.
|
|
27
|
+
*
|
|
28
|
+
* Install calls: reconstructIdeSettings(targetDir, [...existingTemplates, newTemplate], ides)
|
|
29
|
+
* Clear calls: reconstructIdeSettings(targetDir, existingTemplates.filter(t => t !== removed), ides)
|
|
30
|
+
*
|
|
31
|
+
* @param targetDir - Project root directory
|
|
32
|
+
* @param activeTemplates - Template names to include (e.g., ['cc-native', 'bmad'])
|
|
33
|
+
* @param ides - IDEs to reconstruct for (e.g., ['claude', 'windsurf'])
|
|
34
|
+
*/
|
|
35
|
+
export declare function reconstructIdeSettings(targetDir: string, activeTemplates: string[], ides: string[]): Promise<void>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified reconstruction of shared IDE settings files.
|
|
3
|
+
*
|
|
4
|
+
* Both `aiw init` and `aiw clear` use the same operation: reconstruct shared
|
|
5
|
+
* settings from the union of all active templates. Install adds a template to
|
|
6
|
+
* the list; clear removes one. The reconstruction logic is identical.
|
|
7
|
+
*
|
|
8
|
+
* Template files fall into two categories:
|
|
9
|
+
* 1. **Method-owned** — lives in method-namespaced paths (e.g., `.claude/commands/cc-native/`),
|
|
10
|
+
* owned exclusively by one template. Handled by copy/delete, not by this module.
|
|
11
|
+
* 2. **Shared** — lives in common locations (e.g., `settings.json`), multiple templates
|
|
12
|
+
* contribute. Handled by this module via reconstruction from source.
|
|
13
|
+
*
|
|
14
|
+
* @module lib/template-settings-reconstructor
|
|
15
|
+
*/
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { mergeClaudeSettings } from './hooks-merger.js';
|
|
19
|
+
import { IdePathResolver } from './ide-path-resolver.js';
|
|
20
|
+
import { readClaudeSettings, writeClaudeSettings } from './settings-hierarchy.js';
|
|
21
|
+
import { getTemplatePath } from './template-resolver.js';
|
|
22
|
+
import { getTargetHooksFile, readWindsurfHooks, writeWindsurfHooks } from './windsurf-hooks-hierarchy.js';
|
|
23
|
+
import { mergeWindsurfHooks } from './windsurf-hooks-merger.js';
|
|
24
|
+
/**
|
|
25
|
+
* Get the path to the _shared template directory.
|
|
26
|
+
*
|
|
27
|
+
* @returns Absolute path to the _shared template
|
|
28
|
+
*/
|
|
29
|
+
function getSharedTemplatePath() {
|
|
30
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
31
|
+
const currentDir = dirname(currentFilePath);
|
|
32
|
+
return join(currentDir, '..', 'templates', '_shared');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reconstruct .claude/settings.json and .windsurf/hooks.json from the union
|
|
36
|
+
* of all specified templates.
|
|
37
|
+
*
|
|
38
|
+
* The function:
|
|
39
|
+
* 1. Starts with empty settings
|
|
40
|
+
* 2. Merges _shared template settings (always included)
|
|
41
|
+
* 3. For each active template, merges its template-source settings
|
|
42
|
+
* 4. Writes the result to the IDE settings file
|
|
43
|
+
*
|
|
44
|
+
* Uses mergeClaudeSettings() from hooks-merger.ts for dedup-aware merging.
|
|
45
|
+
*
|
|
46
|
+
* Install calls: reconstructIdeSettings(targetDir, [...existingTemplates, newTemplate], ides)
|
|
47
|
+
* Clear calls: reconstructIdeSettings(targetDir, existingTemplates.filter(t => t !== removed), ides)
|
|
48
|
+
*
|
|
49
|
+
* @param targetDir - Project root directory
|
|
50
|
+
* @param activeTemplates - Template names to include (e.g., ['cc-native', 'bmad'])
|
|
51
|
+
* @param ides - IDEs to reconstruct for (e.g., ['claude', 'windsurf'])
|
|
52
|
+
*/
|
|
53
|
+
export async function reconstructIdeSettings(targetDir, activeTemplates, ides) {
|
|
54
|
+
const sharedTemplatePath = getSharedTemplatePath();
|
|
55
|
+
if (ides.includes('claude')) {
|
|
56
|
+
await reconstructClaudeSettings(targetDir, activeTemplates, sharedTemplatePath);
|
|
57
|
+
}
|
|
58
|
+
if (ides.includes('windsurf')) {
|
|
59
|
+
await reconstructWindsurfHooks(targetDir, activeTemplates, sharedTemplatePath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Reconstruct .claude/settings.json from scratch using template sources.
|
|
64
|
+
*/
|
|
65
|
+
async function reconstructClaudeSettings(targetDir, activeTemplates, sharedTemplatePath) {
|
|
66
|
+
const resolver = new IdePathResolver(targetDir);
|
|
67
|
+
const settingsPath = resolver.getClaudeSettings();
|
|
68
|
+
// Read existing settings to preserve non-template fields (methods tracking, etc.)
|
|
69
|
+
const existingSettings = await readClaudeSettings(settingsPath);
|
|
70
|
+
// Preserve the methods tracking from existing settings
|
|
71
|
+
const methodsTracking = existingSettings?.methods;
|
|
72
|
+
// Start from empty and merge all template settings
|
|
73
|
+
let reconstructed = {};
|
|
74
|
+
// 1. Merge _shared template settings
|
|
75
|
+
const sharedSettingsPath = join(sharedTemplatePath, '.claude', 'settings.json');
|
|
76
|
+
const sharedSettings = await readClaudeSettings(sharedSettingsPath);
|
|
77
|
+
if (sharedSettings) {
|
|
78
|
+
reconstructed = mergeClaudeSettings(reconstructed, sharedSettings);
|
|
79
|
+
}
|
|
80
|
+
// 2. Merge each active template's settings (sequential for deterministic merge order)
|
|
81
|
+
for (const template of activeTemplates) {
|
|
82
|
+
try {
|
|
83
|
+
const templatePath = await getTemplatePath(template); // eslint-disable-line no-await-in-loop
|
|
84
|
+
const templateSettingsPath = join(templatePath, '.claude', 'settings.json');
|
|
85
|
+
const templateSettings = await readClaudeSettings(templateSettingsPath); // eslint-disable-line no-await-in-loop
|
|
86
|
+
if (templateSettings) {
|
|
87
|
+
reconstructed = mergeClaudeSettings(reconstructed, templateSettings);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Template not found — skip
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 3. Restore methods tracking
|
|
95
|
+
if (methodsTracking && Object.keys(methodsTracking).length > 0) {
|
|
96
|
+
reconstructed.methods = methodsTracking;
|
|
97
|
+
}
|
|
98
|
+
// 4. Write reconstructed settings
|
|
99
|
+
await writeClaudeSettings(settingsPath, reconstructed);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Reconstruct .windsurf/hooks.json from scratch using template sources.
|
|
103
|
+
*/
|
|
104
|
+
async function reconstructWindsurfHooks(targetDir, activeTemplates, sharedTemplatePath) {
|
|
105
|
+
const hooksPath = getTargetHooksFile(targetDir);
|
|
106
|
+
// Start from empty
|
|
107
|
+
let reconstructed = { hooks: {} };
|
|
108
|
+
// 1. Merge _shared template hooks
|
|
109
|
+
const sharedHooksPath = join(sharedTemplatePath, '.windsurf', 'hooks.json');
|
|
110
|
+
const sharedHooks = await readWindsurfHooks(sharedHooksPath);
|
|
111
|
+
if (sharedHooks) {
|
|
112
|
+
reconstructed = mergeWindsurfHooks(reconstructed, sharedHooks);
|
|
113
|
+
}
|
|
114
|
+
// 2. Merge each active template's hooks (sequential for deterministic merge order)
|
|
115
|
+
for (const template of activeTemplates) {
|
|
116
|
+
try {
|
|
117
|
+
const templatePath = await getTemplatePath(template); // eslint-disable-line no-await-in-loop
|
|
118
|
+
const templateHooksPath = join(templatePath, '.windsurf', 'hooks.json');
|
|
119
|
+
const templateHooks = await readWindsurfHooks(templateHooksPath); // eslint-disable-line no-await-in-loop
|
|
120
|
+
if (templateHooks) {
|
|
121
|
+
reconstructed = mergeWindsurfHooks(reconstructed, templateHooks);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Template not found — skip
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 3. Write reconstructed hooks
|
|
129
|
+
await writeWindsurfHooks(hooksPath, reconstructed);
|
|
130
|
+
}
|
package/dist/templates/CLAUDE.md
CHANGED
|
@@ -22,9 +22,9 @@ Each template installs into `.aiwcli/` (method files) and `.{ide}/` (IDE integra
|
|
|
22
22
|
```
|
|
23
23
|
packages/cli/src/templates/
|
|
24
24
|
├── _shared/ # Cross-method infrastructure (installed by all methods)
|
|
25
|
-
│ ├── hooks/
|
|
26
|
-
│ └── lib/
|
|
27
|
-
│ ├── base/ # Core:
|
|
25
|
+
│ ├── hooks-ts/ # Shared TypeScript hook scripts (context, tasks, sessions)
|
|
26
|
+
│ └── lib-ts/ # Shared TypeScript libraries
|
|
27
|
+
│ ├── base/ # Core: atomic-write, constants, inference, utils
|
|
28
28
|
│ ├── context/ # Context CRUD, selection, formatting, plans, tasks
|
|
29
29
|
│ ├── handoff/ # Session handoff document generation
|
|
30
30
|
│ └── templates/ # Output formatters, plan context templates
|
|
@@ -85,7 +85,7 @@ When multiple templates install, settings.json files merge:
|
|
|
85
85
|
|
|
86
86
|
## Hooks
|
|
87
87
|
|
|
88
|
-
**Location:** Hooks live in `.aiwcli/_shared/hooks/` (cross-method) and `.aiwcli/_{method}/hooks/` (method-specific). They are configured in `.{ide}/settings.json`, not placed in IDE directories.
|
|
88
|
+
**Location:** Hooks live in `.aiwcli/_shared/hooks-ts/` (cross-method, TypeScript) and `.aiwcli/_{method}/hooks/` (method-specific). They are configured in `.{ide}/settings.json`, not placed in IDE directories.
|
|
89
89
|
|
|
90
90
|
**Configuration:**
|
|
91
91
|
```json
|
|
@@ -93,14 +93,14 @@ When multiple templates install, settings.json files merge:
|
|
|
93
93
|
"hooks": {
|
|
94
94
|
"PostToolUse": [{
|
|
95
95
|
"matcher": "Write",
|
|
96
|
-
"hooks": [{ "type": "command", "command": "
|
|
96
|
+
"hooks": [{ "type": "command", "command": "bun run .aiwcli/_cc-native/hooks/cc-native-plan-review.ts", "timeout": 300000 }]
|
|
97
97
|
}]
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
**Requirements:**
|
|
103
|
-
- Prefix method-specific hooks with method name (e.g., `cc-native-plan-review.
|
|
103
|
+
- Prefix method-specific hooks with method name (e.g., `cc-native-plan-review.ts`)
|
|
104
104
|
- Use relative paths from project root
|
|
105
105
|
- Write outputs to `_output/{method}/`
|
|
106
106
|
- Specify timeouts
|
|
@@ -161,7 +161,7 @@ Load and execute `_{method}/workflows/{name}.md`.
|
|
|
161
161
|
| Template file | `UPPERCASE.md.template` | `PROJECT.md.template` |
|
|
162
162
|
| Workflow file | `kebab-case.md` | `new-project.md` |
|
|
163
163
|
| Output file | `UPPERCASE.md` | `PROJECT.md` |
|
|
164
|
-
| Hook file | `{method}-{name}.{ext}` | `gsd-plan-review.
|
|
164
|
+
| Hook file | `{method}-{name}.{ext}` | `gsd-plan-review.ts` |
|
|
165
165
|
| Settings key | `{method}` | `"gsd": {}` |
|
|
166
166
|
| Readme | `{METHOD}-README.md` | `GSD-README.md` |
|
|
167
167
|
|
|
@@ -202,4 +202,4 @@ Load and execute `_{method}/workflows/{name}.md`.
|
|
|
202
202
|
- Full workflows in IDE command files
|
|
203
203
|
- Hardcoded paths without method namespace
|
|
204
204
|
- Putting hook scripts directly in IDE directories (`.claude/hooks/`)
|
|
205
|
-
- Creating `_shared/` directories inside method templates (e.g., `cc-native/_shared/`). All shared code lives in `packages/cli/src/templates/_shared/`. Method templates reference shared code via
|
|
205
|
+
- Creating `_shared/` directories inside method templates (e.g., `cc-native/_shared/`). All shared code lives in `packages/cli/src/templates/_shared/`. Method templates reference shared code via imports at runtime, not by copying.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Resume Handoff
|
|
2
|
+
|
|
3
|
+
Restore session context from a handoff document programmatically, then create actionable ISC tasks so work continues without re-discovering what was already learned.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
|
|
7
|
+
- `$ARGUMENTS` - Optional path to a handoff folder or index.md. If omitted, auto-discovers the most recent handoff from the active context.
|
|
8
|
+
|
|
9
|
+
## Instructions
|
|
10
|
+
|
|
11
|
+
### Step 1: Gather Context via Script
|
|
12
|
+
|
|
13
|
+
Run the resume script to collect and format all handoff sections:
|
|
14
|
+
|
|
15
|
+
**If `$ARGUMENTS` is provided:**
|
|
16
|
+
```bash
|
|
17
|
+
bun .aiwcli/_shared/scripts/resume_handoff.ts "$ARGUMENTS"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**If `$ARGUMENTS` is empty:**
|
|
21
|
+
The script auto-discovers the active context ID programmatically — no manual lookup needed:
|
|
22
|
+
```bash
|
|
23
|
+
bun .aiwcli/_shared/scripts/resume_handoff.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Present the script's output to the conversation. The output is already structured in priority order (dead ends first, then pending items, decisions, git delta, completed work, context notes).
|
|
27
|
+
|
|
28
|
+
If the script exits with an error, show the error message and stop.
|
|
29
|
+
|
|
30
|
+
### Step 2: Create ISC Tasks
|
|
31
|
+
|
|
32
|
+
Convert each actionable item from the script output into a task via `TaskCreate`:
|
|
33
|
+
- Each pending issue from the **Pending Items** section
|
|
34
|
+
- Each remaining plan item from the **Plan — Remaining Items** section
|
|
35
|
+
|
|
36
|
+
Each task follows ISC format — ~8 words, states a desired end-state (not an action), and is binary testable in 2 seconds.
|
|
37
|
+
|
|
38
|
+
**Example:**
|
|
39
|
+
|
|
40
|
+
| Source | ISC Task |
|
|
41
|
+
|--------|----------|
|
|
42
|
+
| Pending: "Fix race condition in SessionStore" | "SessionStore handles concurrent access without race conditions" |
|
|
43
|
+
| Plan remaining: "Add retry logic to API client" | "API client retries failed requests with exponential backoff" |
|
|
44
|
+
| Next step: "Write tests for auth flow" | "Auth flow has passing integration test coverage" |
|
|
45
|
+
|
|
46
|
+
### Step 3: Confirm Ready
|
|
47
|
+
|
|
48
|
+
After creating tasks, run `TaskList` and confirm ready to continue.
|
|
49
|
+
|
|
50
|
+
## Constraints
|
|
51
|
+
|
|
52
|
+
- Dead ends are presented verbatim from the script output — never summarize or omit entries
|
|
53
|
+
- ISC tasks use ~8-word end-state format, not action descriptions
|
|
54
|
+
- Skip missing files gracefully (the script handles this)
|
|
55
|
+
- If the script warns about staleness (>7 days), surface the warning prominently
|
|
56
|
+
|
|
57
|
+
## Success Criteria
|
|
58
|
+
|
|
59
|
+
- [ ] Script ran successfully and output the structured briefing
|
|
60
|
+
- [ ] Dead ends visible in conversation (not summarized)
|
|
61
|
+
- [ ] Pending items converted to ISC tasks via TaskCreate
|
|
62
|
+
- [ ] Plan completion percentage visible (if plan exists)
|
|
63
|
+
- [ ] Git delta visible
|
|
64
|
+
- [ ] Tasks confirmed via TaskList
|