@nemus-cli/nemus 0.2.1
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/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.md +275 -0
- package/assets/README.md +53 -0
- package/assets/banner.png +0 -0
- package/assets/banner.svg +66 -0
- package/assets/favicon-32.png +0 -0
- package/assets/favicon-64.png +0 -0
- package/assets/icon-256.png +0 -0
- package/assets/icon.svg +23 -0
- package/assets/logo-256.png +0 -0
- package/assets/logo-512.png +0 -0
- package/assets/logo-wordmark.svg +35 -0
- package/assets/logo.svg +32 -0
- package/bin/workspace.js +130 -0
- package/dist/cli/ai-prompt.js +421 -0
- package/dist/cli/dashboard/AgentList.js +61 -0
- package/dist/cli/dashboard/DashboardSidebar.js +280 -0
- package/dist/cli/dashboard/HelpFooter.js +73 -0
- package/dist/cli/dashboard/StatusBadge.js +50 -0
- package/dist/cli/dashboard/index.js +11 -0
- package/dist/cli/dashboard/types.js +2 -0
- package/dist/cli/tui.js +48 -0
- package/dist/commands/ai-prompt.js +22 -0
- package/dist/commands/analyze-deps.js +138 -0
- package/dist/commands/archive.js +113 -0
- package/dist/commands/branch/create.js +119 -0
- package/dist/commands/branch/index.js +104 -0
- package/dist/commands/branch/merge.js +90 -0
- package/dist/commands/branch/rebase.js +84 -0
- package/dist/commands/branch/switch.js +165 -0
- package/dist/commands/cache/index.js +80 -0
- package/dist/commands/cache/manager.js +198 -0
- package/dist/commands/cleanup.js +153 -0
- package/dist/commands/configure-claude.js +55 -0
- package/dist/commands/configure.js +231 -0
- package/dist/commands/create.js +204 -0
- package/dist/commands/dashboard/hook-handler.js +162 -0
- package/dist/commands/dashboard/index.js +258 -0
- package/dist/commands/dashboard/launcher.js +40 -0
- package/dist/commands/dashboard/session-picker.js +106 -0
- package/dist/commands/dashboard/sidebar.js +39 -0
- package/dist/commands/dashboard/workspace-picker.js +95 -0
- package/dist/commands/delete.js +194 -0
- package/dist/commands/deprecated-aliases.js +44 -0
- package/dist/commands/diff.js +129 -0
- package/dist/commands/doctor.js +141 -0
- package/dist/commands/generate-docs.js +77 -0
- package/dist/commands/ghq-status.js +72 -0
- package/dist/commands/go.js +154 -0
- package/dist/commands/history.js +124 -0
- package/dist/commands/list.js +191 -0
- package/dist/commands/mcp/index.js +69 -0
- package/dist/commands/migrate.js +296 -0
- package/dist/commands/remove-repo.js +217 -0
- package/dist/commands/report-bug.js +112 -0
- package/dist/commands/run.js +147 -0
- package/dist/commands/save-context.js +171 -0
- package/dist/commands/sessions.js +129 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/suite/create.js +212 -0
- package/dist/commands/suite/delete.js +64 -0
- package/dist/commands/suite/export.js +126 -0
- package/dist/commands/suite/import.js +128 -0
- package/dist/commands/suite/index.js +94 -0
- package/dist/commands/suite/list.js +49 -0
- package/dist/commands/suite/use.js +227 -0
- package/dist/commands/sync.js +117 -0
- package/dist/commands/update.js +203 -0
- package/dist/mcp/install.js +550 -0
- package/dist/mcp/server.js +398 -0
- package/dist/mcp/tools.js +922 -0
- package/dist/pi-extensions/permission-sync.ts.template +41 -0
- package/dist/pi-extensions/permissions-gate.ts.template +69 -0
- package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/dist/pi-extensions/review-reminder.ts.template +58 -0
- package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
- package/dist/program.js +136 -0
- package/dist/scripts/workspace-table.py +394 -0
- package/dist/types/dashboard.js +11 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/agent-config.js +263 -0
- package/dist/utils/agent-state.js +178 -0
- package/dist/utils/banner.js +24 -0
- package/dist/utils/branch-operations.js +232 -0
- package/dist/utils/bug-report.js +355 -0
- package/dist/utils/cache.js +168 -0
- package/dist/utils/claude-integration.js +617 -0
- package/dist/utils/claude-sessions.js +260 -0
- package/dist/utils/cleanup-operations.js +120 -0
- package/dist/utils/colors.js +26 -0
- package/dist/utils/command-helpers.js +44 -0
- package/dist/utils/config.js +150 -0
- package/dist/utils/context-file.js +33 -0
- package/dist/utils/dashboard-hooks.js +172 -0
- package/dist/utils/dependency-analyzer.js +234 -0
- package/dist/utils/diff-operations.js +80 -0
- package/dist/utils/doc-generator.js +184 -0
- package/dist/utils/ghq-integration.js +332 -0
- package/dist/utils/git-operations.js +237 -0
- package/dist/utils/git-status.js +187 -0
- package/dist/utils/github.js +68 -0
- package/dist/utils/health-checks.js +345 -0
- package/dist/utils/history.js +150 -0
- package/dist/utils/hooks.js +100 -0
- package/dist/utils/logger.js +40 -0
- package/dist/utils/permission-sync.js +681 -0
- package/dist/utils/pi-extensions.js +224 -0
- package/dist/utils/progress.js +92 -0
- package/dist/utils/prompts.js +279 -0
- package/dist/utils/repo-resolver.js +260 -0
- package/dist/utils/retry.js +65 -0
- package/dist/utils/run-operations.js +30 -0
- package/dist/utils/suite.js +159 -0
- package/dist/utils/sync-operations.js +107 -0
- package/dist/utils/tmux-dashboard.js +184 -0
- package/dist/utils/validation.js +153 -0
- package/dist/utils/version-check.js +121 -0
- package/dist/utils/workspace-meta.js +185 -0
- package/install-shell-integration.sh +532 -0
- package/install.sh +401 -0
- package/package.json +90 -0
- package/remove-shell-block.awk +44 -0
- package/scripts/postinstall.js +85 -0
- package/skills/analyze-deps.md +23 -0
- package/skills/archive-workspace.md +26 -0
- package/skills/branch-create.md +18 -0
- package/skills/create-workspace.md +39 -0
- package/skills/delete-workspace.md +37 -0
- package/skills/list-org-repos.md +26 -0
- package/skills/list-suites.md +24 -0
- package/skills/list-workspaces.md +27 -0
- package/skills/refresh-workspace-docs.md +89 -0
- package/skills/remove-repo.md +25 -0
- package/skills/run-command.md +23 -0
- package/skills/save-context.md +68 -0
- package/skills/search-repos.md +31 -0
- package/skills/snapshot-list.md +18 -0
- package/skills/snapshot-save.md +12 -0
- package/skills/switch-branch.md +18 -0
- package/skills/update-cache.md +21 -0
- package/skills/update-workspace.md +66 -0
- package/skills/workspace-cleanup.md +18 -0
- package/skills/workspace-diff.md +23 -0
- package/skills/workspace-doctor.md +23 -0
- package/skills/workspace-info.md +45 -0
- package/skills/workspace-manager/SKILL.md +201 -0
- package/skills/workspace-manager/references/ai-prompt.md +44 -0
- package/skills/workspace-manager/references/analyze-deps.md +45 -0
- package/skills/workspace-manager/references/archive-workspace.md +38 -0
- package/skills/workspace-manager/references/branch-create.md +31 -0
- package/skills/workspace-manager/references/branch-merge.md +22 -0
- package/skills/workspace-manager/references/branch-rebase.md +22 -0
- package/skills/workspace-manager/references/cache-clear.md +18 -0
- package/skills/workspace-manager/references/cache-info.md +22 -0
- package/skills/workspace-manager/references/configure-claude.md +31 -0
- package/skills/workspace-manager/references/configure.md +35 -0
- package/skills/workspace-manager/references/create-workspace.md +45 -0
- package/skills/workspace-manager/references/dashboard.md +61 -0
- package/skills/workspace-manager/references/delete-workspace.md +33 -0
- package/skills/workspace-manager/references/generate-docs.md +36 -0
- package/skills/workspace-manager/references/ghq-status.md +28 -0
- package/skills/workspace-manager/references/go.md +33 -0
- package/skills/workspace-manager/references/history.md +41 -0
- package/skills/workspace-manager/references/list-org-repos.md +26 -0
- package/skills/workspace-manager/references/list-suites.md +21 -0
- package/skills/workspace-manager/references/list-workspaces.md +27 -0
- package/skills/workspace-manager/references/mcp.md +49 -0
- package/skills/workspace-manager/references/remove-repo.md +26 -0
- package/skills/workspace-manager/references/run-command.md +44 -0
- package/skills/workspace-manager/references/search-repos.md +24 -0
- package/skills/workspace-manager/references/sessions.md +27 -0
- package/skills/workspace-manager/references/suite-create.md +22 -0
- package/skills/workspace-manager/references/suite-delete.md +21 -0
- package/skills/workspace-manager/references/suite-export.md +19 -0
- package/skills/workspace-manager/references/suite-import.md +19 -0
- package/skills/workspace-manager/references/suite-use.md +27 -0
- package/skills/workspace-manager/references/switch-branch.md +25 -0
- package/skills/workspace-manager/references/update-cache.md +22 -0
- package/skills/workspace-manager/references/update-workspace.md +50 -0
- package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
- package/skills/workspace-manager/references/workspace-diff.md +27 -0
- package/skills/workspace-manager/references/workspace-doctor.md +27 -0
- package/skills/workspace-manager/references/workspace-info.md +33 -0
- package/skills/workspace-manager/references/workspace-status.md +43 -0
- package/skills/workspace-manager/references/workspace-sync.md +26 -0
- package/skills/workspace-status.md +26 -0
- package/skills/workspace-sync.md +23 -0
- package/src/cli/ai-prompt.test.ts +667 -0
- package/src/cli/ai-prompt.ts +421 -0
- package/src/cli/dashboard/AgentList.tsx +44 -0
- package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
- package/src/cli/dashboard/HelpFooter.tsx +22 -0
- package/src/cli/dashboard/StatusBadge.tsx +23 -0
- package/src/cli/dashboard/index.ts +5 -0
- package/src/cli/dashboard/types.ts +16 -0
- package/src/cli/tui.tsx +51 -0
- package/src/cli/workspace-cli.test.ts +30 -0
- package/src/commands/ai-prompt.ts +21 -0
- package/src/commands/analyze-deps.ts +119 -0
- package/src/commands/archive.ts +114 -0
- package/src/commands/branch/create.ts +94 -0
- package/src/commands/branch/index.ts +74 -0
- package/src/commands/branch/merge.ts +61 -0
- package/src/commands/branch/rebase.ts +54 -0
- package/src/commands/branch/switch.ts +150 -0
- package/src/commands/cache/index.ts +51 -0
- package/src/commands/cache/manager.ts +168 -0
- package/src/commands/cleanup.ts +132 -0
- package/src/commands/configure-claude.ts +55 -0
- package/src/commands/configure.ts +204 -0
- package/src/commands/create.ts +196 -0
- package/src/commands/dashboard/hook-handler.ts +138 -0
- package/src/commands/dashboard/index.ts +248 -0
- package/src/commands/dashboard/launcher.ts +42 -0
- package/src/commands/dashboard/session-picker.ts +73 -0
- package/src/commands/dashboard/sidebar.js +39 -0
- package/src/commands/dashboard/workspace-picker.ts +62 -0
- package/src/commands/delete.test.ts +250 -0
- package/src/commands/delete.ts +171 -0
- package/src/commands/deprecated-aliases.ts +55 -0
- package/src/commands/diff.ts +106 -0
- package/src/commands/doctor.ts +110 -0
- package/src/commands/generate-docs.ts +45 -0
- package/src/commands/ghq-status.ts +70 -0
- package/src/commands/go.ts +125 -0
- package/src/commands/history.ts +136 -0
- package/src/commands/list.test.ts +219 -0
- package/src/commands/list.ts +176 -0
- package/src/commands/mcp/index.ts +39 -0
- package/src/commands/migrate.ts +277 -0
- package/src/commands/remove-repo.ts +194 -0
- package/src/commands/report-bug.ts +88 -0
- package/src/commands/run.ts +125 -0
- package/src/commands/save-context.test.ts +113 -0
- package/src/commands/save-context.ts +141 -0
- package/src/commands/sessions.ts +99 -0
- package/src/commands/status.ts +111 -0
- package/src/commands/suite/create.ts +240 -0
- package/src/commands/suite/delete.ts +65 -0
- package/src/commands/suite/export.ts +97 -0
- package/src/commands/suite/import.ts +100 -0
- package/src/commands/suite/index.ts +66 -0
- package/src/commands/suite/list.ts +54 -0
- package/src/commands/suite/use.ts +232 -0
- package/src/commands/sync.ts +97 -0
- package/src/commands/update.ts +197 -0
- package/src/mcp/install.ts +529 -0
- package/src/mcp/server.ts +579 -0
- package/src/mcp/tools.test.ts +545 -0
- package/src/mcp/tools.ts +1019 -0
- package/src/mcp/update-workspace.test.ts +220 -0
- package/src/pi-extensions/permission-sync.ts.template +41 -0
- package/src/pi-extensions/permissions-gate.ts.template +69 -0
- package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/src/pi-extensions/review-reminder.ts.template +58 -0
- package/src/pi-extensions/workspace-input-status.ts.template +919 -0
- package/src/program.ts +112 -0
- package/src/scripts/workspace-table.py +394 -0
- package/src/types/dashboard.ts +29 -0
- package/src/types/index.ts +109 -0
- package/src/types/ink.d.ts +171 -0
- package/src/utils/agent-config.test.ts +303 -0
- package/src/utils/agent-config.ts +264 -0
- package/src/utils/agent-state.test.ts +166 -0
- package/src/utils/agent-state.ts +135 -0
- package/src/utils/banner.ts +24 -0
- package/src/utils/branch-operations.ts +237 -0
- package/src/utils/bug-report.test.ts +174 -0
- package/src/utils/bug-report.ts +355 -0
- package/src/utils/cache.ts +150 -0
- package/src/utils/claude-integration.test.ts +381 -0
- package/src/utils/claude-integration.ts +649 -0
- package/src/utils/claude-sessions.test.ts +262 -0
- package/src/utils/claude-sessions.ts +251 -0
- package/src/utils/cleanup-operations.ts +94 -0
- package/src/utils/colors.ts +25 -0
- package/src/utils/command-helpers.ts +46 -0
- package/src/utils/config.test.ts +203 -0
- package/src/utils/config.ts +133 -0
- package/src/utils/context-file.ts +36 -0
- package/src/utils/dashboard-hooks.test.ts +168 -0
- package/src/utils/dashboard-hooks.ts +163 -0
- package/src/utils/dependency-analyzer.ts +243 -0
- package/src/utils/diff-operations.ts +59 -0
- package/src/utils/doc-generator.ts +187 -0
- package/src/utils/ghq-integration.test.ts +195 -0
- package/src/utils/ghq-integration.ts +317 -0
- package/src/utils/git-operations.test.ts +265 -0
- package/src/utils/git-operations.ts +233 -0
- package/src/utils/git-status.ts +171 -0
- package/src/utils/github.ts +74 -0
- package/src/utils/health-checks.ts +359 -0
- package/src/utils/history.ts +140 -0
- package/src/utils/hooks.ts +92 -0
- package/src/utils/logger.ts +37 -0
- package/src/utils/permission-sync.test.ts +593 -0
- package/src/utils/permission-sync.ts +720 -0
- package/src/utils/pi-extensions.test.ts +182 -0
- package/src/utils/pi-extensions.ts +226 -0
- package/src/utils/progress.ts +73 -0
- package/src/utils/prompts.test.ts +169 -0
- package/src/utils/prompts.ts +296 -0
- package/src/utils/repo-resolver.test.ts +313 -0
- package/src/utils/repo-resolver.ts +294 -0
- package/src/utils/retry.test.ts +124 -0
- package/src/utils/retry.ts +85 -0
- package/src/utils/run-operations.ts +38 -0
- package/src/utils/suite.test.ts +256 -0
- package/src/utils/suite.ts +144 -0
- package/src/utils/sync-operations.ts +87 -0
- package/src/utils/tmux-dashboard.ts +193 -0
- package/src/utils/validation.test.ts +139 -0
- package/src/utils/validation.ts +128 -0
- package/src/utils/version-check.test.ts +108 -0
- package/src/utils/version-check.ts +96 -0
- package/src/utils/workspace-meta.ts +173 -0
- package/sync-permissions.sh +137 -0
- package/tsconfig.json +20 -0
- package/uninstall.sh +162 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { HOOK_MARKER } from '../types/dashboard';
|
|
5
|
+
|
|
6
|
+
const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
7
|
+
|
|
8
|
+
/** Events we register hooks for */
|
|
9
|
+
const HOOK_EVENTS = ['SessionStart', 'PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'Notification', 'Stop', 'SessionEnd'] as const;
|
|
10
|
+
|
|
11
|
+
interface HookEntry {
|
|
12
|
+
type: string;
|
|
13
|
+
command: string;
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface HookGroup {
|
|
18
|
+
matcher?: string;
|
|
19
|
+
hooks: HookEntry[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ClaudeSettings {
|
|
23
|
+
hooks?: Record<string, HookGroup[]>;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve path to the compiled hook-handler.js.
|
|
29
|
+
* This file (dashboard-hooks.ts) compiles to dist/utils/dashboard-hooks.js,
|
|
30
|
+
* so hook-handler.js is at ../commands/dashboard/hook-handler.js relative to dist/utils/.
|
|
31
|
+
*/
|
|
32
|
+
function getHookHandlerPath(): string {
|
|
33
|
+
return path.join(__dirname, '..', 'commands', 'dashboard', 'hook-handler.js');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build the hook command string for a given event.
|
|
38
|
+
*/
|
|
39
|
+
function buildHookCommand(event: string): string {
|
|
40
|
+
const handlerPath = getHookHandlerPath();
|
|
41
|
+
return `node "${handlerPath}" --event ${event} --marker ${HOOK_MARKER}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read Claude settings from disk. Returns empty settings if file doesn't exist.
|
|
46
|
+
*/
|
|
47
|
+
function readClaudeSettings(): ClaudeSettings {
|
|
48
|
+
try {
|
|
49
|
+
const content = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8');
|
|
50
|
+
return JSON.parse(content);
|
|
51
|
+
} catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Write Claude settings to disk atomically (write to .tmp, then rename).
|
|
58
|
+
* Prevents corruption if the process is interrupted mid-write.
|
|
59
|
+
*/
|
|
60
|
+
function writeClaudeSettings(settings: ClaudeSettings): void {
|
|
61
|
+
const dir = path.dirname(CLAUDE_SETTINGS_PATH);
|
|
62
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
63
|
+
const tmp = CLAUDE_SETTINGS_PATH + '.tmp';
|
|
64
|
+
try {
|
|
65
|
+
fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
66
|
+
fs.renameSync(tmp, CLAUDE_SETTINGS_PATH);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
try { fs.unlinkSync(tmp); } catch { /* ignore */ }
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if a hook group contains our dashboard hook marker.
|
|
75
|
+
*/
|
|
76
|
+
function groupContainsMarker(group: HookGroup): boolean {
|
|
77
|
+
return group.hooks.some(h => h.command && h.command.includes(HOOK_MARKER));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Install dashboard hooks into Claude settings.
|
|
82
|
+
* Appends new hook groups — never modifies existing hooks.
|
|
83
|
+
* Idempotent: skips events that already have the marker.
|
|
84
|
+
*/
|
|
85
|
+
export function installDashboardHooks(): void {
|
|
86
|
+
const settings = readClaudeSettings();
|
|
87
|
+
|
|
88
|
+
if (!settings.hooks) {
|
|
89
|
+
settings.hooks = {};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const event of HOOK_EVENTS) {
|
|
93
|
+
if (!settings.hooks[event]) {
|
|
94
|
+
settings.hooks[event] = [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if already installed
|
|
98
|
+
const alreadyInstalled = settings.hooks[event].some(groupContainsMarker);
|
|
99
|
+
if (alreadyInstalled) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Append a new hook group
|
|
104
|
+
const hookGroup: HookGroup = {
|
|
105
|
+
hooks: [
|
|
106
|
+
{
|
|
107
|
+
type: 'command',
|
|
108
|
+
command: buildHookCommand(event),
|
|
109
|
+
timeout: 5,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// PreToolUse and PostToolUse hooks use a matcher — use empty string to match all tools
|
|
115
|
+
if (event === 'PreToolUse' || event === 'PostToolUse') {
|
|
116
|
+
hookGroup.matcher = '';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
settings.hooks[event].push(hookGroup);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
writeClaudeSettings(settings);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if all dashboard hooks are installed.
|
|
127
|
+
*/
|
|
128
|
+
export function areDashboardHooksInstalled(): boolean {
|
|
129
|
+
const settings = readClaudeSettings();
|
|
130
|
+
if (!settings.hooks) return false;
|
|
131
|
+
|
|
132
|
+
return HOOK_EVENTS.every(event => {
|
|
133
|
+
const groups = settings.hooks?.[event];
|
|
134
|
+
if (!groups || !Array.isArray(groups)) return false;
|
|
135
|
+
return groups.some(groupContainsMarker);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Remove all dashboard hooks from Claude settings.
|
|
141
|
+
* Only removes hook groups containing the HOOK_MARKER — leaves everything else untouched.
|
|
142
|
+
*/
|
|
143
|
+
export function uninstallDashboardHooks(): void {
|
|
144
|
+
const settings = readClaudeSettings();
|
|
145
|
+
if (!settings.hooks) return;
|
|
146
|
+
|
|
147
|
+
for (const event of HOOK_EVENTS) {
|
|
148
|
+
if (!settings.hooks[event]) continue;
|
|
149
|
+
settings.hooks[event] = settings.hooks[event].filter(g => !groupContainsMarker(g));
|
|
150
|
+
|
|
151
|
+
// Clean up empty arrays
|
|
152
|
+
if (settings.hooks[event].length === 0) {
|
|
153
|
+
delete settings.hooks[event];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Clean up empty hooks object
|
|
158
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
159
|
+
delete settings.hooks;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
writeClaudeSettings(settings);
|
|
163
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { WorkspaceMetadata, DependencyInfo } from '../types';
|
|
4
|
+
import { getUserConfig } from './config';
|
|
5
|
+
|
|
6
|
+
export interface DependencyAnalysis {
|
|
7
|
+
repoName: string;
|
|
8
|
+
dependencies: string[];
|
|
9
|
+
dependents: string[];
|
|
10
|
+
missingDependencies: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const analyzePackageJson = async (repoPath: string): Promise<string[]> => {
|
|
14
|
+
try {
|
|
15
|
+
const packageJsonPath = path.join(repoPath, 'package.json');
|
|
16
|
+
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
17
|
+
const packageJson = JSON.parse(content);
|
|
18
|
+
|
|
19
|
+
const dependencies: string[] = [];
|
|
20
|
+
|
|
21
|
+
// Check dependencies and devDependencies
|
|
22
|
+
const allDeps = {
|
|
23
|
+
...packageJson.dependencies,
|
|
24
|
+
...packageJson.devDependencies,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const { githubOrg } = getUserConfig();
|
|
28
|
+
const scopePrefix = githubOrg ? `@${githubOrg}/` : '';
|
|
29
|
+
for (const dep of Object.keys(allDeps)) {
|
|
30
|
+
// Match packages under the configured org scope (e.g. @your-org/*), or any
|
|
31
|
+
// scoped package as a fallback so cross-repo links are still detected.
|
|
32
|
+
if (dep.startsWith('@')) {
|
|
33
|
+
dependencies.push(
|
|
34
|
+
scopePrefix && dep.startsWith(scopePrefix)
|
|
35
|
+
? dep.slice(scopePrefix.length)
|
|
36
|
+
: dep.replace(/^@[^/]+\//, '').replace('@', '')
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return dependencies;
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const analyzeImports = async (repoPath: string): Promise<string[]> => {
|
|
48
|
+
try {
|
|
49
|
+
const dependencies: Set<string> = new Set();
|
|
50
|
+
|
|
51
|
+
// Scan source files for relative imports that might indicate dependencies
|
|
52
|
+
const scanDirs = ['src', 'lib', 'app'];
|
|
53
|
+
|
|
54
|
+
for (const dir of scanDirs) {
|
|
55
|
+
const dirPath = path.join(repoPath, dir);
|
|
56
|
+
try {
|
|
57
|
+
await fs.access(dirPath);
|
|
58
|
+
// For simplicity, we're not doing deep file scanning here
|
|
59
|
+
// In a real implementation, you'd recursively read .ts, .js, .tsx files
|
|
60
|
+
} catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Array.from(dependencies);
|
|
66
|
+
} catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const analyzeDockerfile = async (repoPath: string): Promise<string[]> => {
|
|
72
|
+
try {
|
|
73
|
+
const dockerfilePath = path.join(repoPath, 'Dockerfile');
|
|
74
|
+
const content = await fs.readFile(dockerfilePath, 'utf-8');
|
|
75
|
+
|
|
76
|
+
const dependencies: string[] = [];
|
|
77
|
+
const fromRegex = /FROM\s+([^\s]+)/g;
|
|
78
|
+
let match;
|
|
79
|
+
|
|
80
|
+
while ((match = fromRegex.exec(content)) !== null) {
|
|
81
|
+
const image = match[1];
|
|
82
|
+
// Extract potential dependency references to other org images. When an org
|
|
83
|
+
// is configured, treat images named after it (e.g. `your-org-foo`) as deps.
|
|
84
|
+
const { githubOrg } = getUserConfig();
|
|
85
|
+
if (githubOrg && image.includes(githubOrg)) {
|
|
86
|
+
dependencies.push(image.split(':')[0].replace(`${githubOrg}-`, ''));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return dependencies;
|
|
91
|
+
} catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const analyzeDockerCompose = async (repoPath: string): Promise<string[]> => {
|
|
97
|
+
try {
|
|
98
|
+
const composePath = path.join(repoPath, 'docker-compose.yml');
|
|
99
|
+
const content = await fs.readFile(composePath, 'utf-8');
|
|
100
|
+
|
|
101
|
+
const dependencies: string[] = [];
|
|
102
|
+
|
|
103
|
+
// Simple pattern matching for service references
|
|
104
|
+
const serviceRegex = /depends_on:\s*\n((?:\s+-\s+\w+\n)+)/g;
|
|
105
|
+
let match;
|
|
106
|
+
|
|
107
|
+
while ((match = serviceRegex.exec(content)) !== null) {
|
|
108
|
+
const services = match[1].match(/\w+/g);
|
|
109
|
+
if (services) {
|
|
110
|
+
dependencies.push(...services);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return dependencies;
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const analyzeDependencies = async (
|
|
121
|
+
workspacePath: string,
|
|
122
|
+
repoNames: string[]
|
|
123
|
+
): Promise<Map<string, DependencyAnalysis>> => {
|
|
124
|
+
const analyses = new Map<string, DependencyAnalysis>();
|
|
125
|
+
|
|
126
|
+
for (const repoName of repoNames) {
|
|
127
|
+
const repoPath = path.join(workspacePath, repoName);
|
|
128
|
+
|
|
129
|
+
const [packageDeps, dockerDeps, composeDeps] = await Promise.all([
|
|
130
|
+
analyzePackageJson(repoPath),
|
|
131
|
+
analyzeDockerfile(repoPath),
|
|
132
|
+
analyzeDockerCompose(repoPath),
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
const allDeps = [...new Set([...packageDeps, ...dockerDeps, ...composeDeps])];
|
|
136
|
+
|
|
137
|
+
// Filter to only dependencies that are in the workspace
|
|
138
|
+
const validDeps = allDeps.filter(dep => repoNames.includes(dep));
|
|
139
|
+
|
|
140
|
+
// Find missing dependencies (referenced but not in workspace)
|
|
141
|
+
const missingDeps = allDeps.filter(dep => !repoNames.includes(dep));
|
|
142
|
+
|
|
143
|
+
analyses.set(repoName, {
|
|
144
|
+
repoName,
|
|
145
|
+
dependencies: validDeps,
|
|
146
|
+
dependents: [], // Will be filled in next pass
|
|
147
|
+
missingDependencies: missingDeps,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Build dependents lists
|
|
152
|
+
for (const [repoName, analysis] of analyses) {
|
|
153
|
+
for (const dep of analysis.dependencies) {
|
|
154
|
+
const depAnalysis = analyses.get(dep);
|
|
155
|
+
if (depAnalysis && !depAnalysis.dependents.includes(repoName)) {
|
|
156
|
+
depAnalysis.dependents.push(repoName);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return analyses;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const detectCircularDependencies = (
|
|
165
|
+
analyses: Map<string, DependencyAnalysis>
|
|
166
|
+
): string[][] => {
|
|
167
|
+
const visited = new Set<string>();
|
|
168
|
+
const recursionStack = new Set<string>();
|
|
169
|
+
const cycles: string[][] = [];
|
|
170
|
+
|
|
171
|
+
const dfs = (repo: string, path: string[]): void => {
|
|
172
|
+
visited.add(repo);
|
|
173
|
+
recursionStack.add(repo);
|
|
174
|
+
path.push(repo);
|
|
175
|
+
|
|
176
|
+
const analysis = analyses.get(repo);
|
|
177
|
+
if (analysis) {
|
|
178
|
+
for (const dep of analysis.dependencies) {
|
|
179
|
+
if (!visited.has(dep)) {
|
|
180
|
+
dfs(dep, [...path]);
|
|
181
|
+
} else if (recursionStack.has(dep)) {
|
|
182
|
+
// Found a cycle
|
|
183
|
+
const cycleStart = path.indexOf(dep);
|
|
184
|
+
const cycle = path.slice(cycleStart);
|
|
185
|
+
cycle.push(dep);
|
|
186
|
+
cycles.push(cycle);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
recursionStack.delete(repo);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
for (const repoName of analyses.keys()) {
|
|
195
|
+
if (!visited.has(repoName)) {
|
|
196
|
+
dfs(repoName, []);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return cycles;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const generateMermaidDiagram = (analyses: Map<string, DependencyAnalysis>): string => {
|
|
204
|
+
const lines = ['```mermaid', 'graph TD'];
|
|
205
|
+
|
|
206
|
+
for (const [repoName, analysis] of analyses) {
|
|
207
|
+
const nodeId = repoName.replace(/[^a-zA-Z0-9]/g, '_');
|
|
208
|
+
|
|
209
|
+
for (const dep of analysis.dependencies) {
|
|
210
|
+
const depId = dep.replace(/[^a-zA-Z0-9]/g, '_');
|
|
211
|
+
lines.push(` ${nodeId}[${repoName}] --> ${depId}[${dep}]`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Add standalone nodes (no dependencies)
|
|
215
|
+
if (analysis.dependencies.length === 0 && analysis.dependents.length === 0) {
|
|
216
|
+
lines.push(` ${nodeId}[${repoName}]`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
lines.push('```');
|
|
221
|
+
return lines.join('\n');
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export const updateWorkspaceMetadata = (
|
|
225
|
+
metadata: WorkspaceMetadata,
|
|
226
|
+
analyses: Map<string, DependencyAnalysis>
|
|
227
|
+
): WorkspaceMetadata => {
|
|
228
|
+
const dependencies: { [repoName: string]: DependencyInfo } = {};
|
|
229
|
+
|
|
230
|
+
for (const [repoName, analysis] of analyses) {
|
|
231
|
+
dependencies[repoName] = {
|
|
232
|
+
dependsOn: analysis.dependencies,
|
|
233
|
+
dependedBy: analysis.dependents,
|
|
234
|
+
lastAnalyzed: new Date().toISOString(),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
...metadata,
|
|
240
|
+
dependencies,
|
|
241
|
+
lastModified: new Date().toISOString(),
|
|
242
|
+
};
|
|
243
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
export interface DiffStats {
|
|
8
|
+
files: number;
|
|
9
|
+
insertions: number;
|
|
10
|
+
deletions: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface RepoDiff {
|
|
14
|
+
repo: string;
|
|
15
|
+
staged: DiffStats;
|
|
16
|
+
unstaged: DiffStats;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseDiffStat(output: string): DiffStats {
|
|
21
|
+
const match = output.match(/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/);
|
|
22
|
+
if (!match) {
|
|
23
|
+
return { files: 0, insertions: 0, deletions: 0 };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
files: parseInt(match[1], 10) || 0,
|
|
27
|
+
insertions: parseInt(match[2], 10) || 0,
|
|
28
|
+
deletions: parseInt(match[3], 10) || 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getRepoDiff(repoPath: string, repoName: string): Promise<RepoDiff> {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(repoPath);
|
|
35
|
+
} catch {
|
|
36
|
+
return { repo: repoName, staged: { files: 0, insertions: 0, deletions: 0 }, unstaged: { files: 0, insertions: 0, deletions: 0 }, error: 'Directory not found' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const [unstagedResult, stagedResult] = await Promise.all([
|
|
41
|
+
execAsync('git diff --stat', { cwd: repoPath }).catch(() => ({ stdout: '' })),
|
|
42
|
+
execAsync('git diff --cached --stat', { cwd: repoPath }).catch(() => ({ stdout: '' })),
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
repo: repoName,
|
|
47
|
+
unstaged: parseDiffStat((unstagedResult as { stdout: string }).stdout),
|
|
48
|
+
staged: parseDiffStat((stagedResult as { stdout: string }).stdout),
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
52
|
+
return {
|
|
53
|
+
repo: repoName,
|
|
54
|
+
staged: { files: 0, insertions: 0, deletions: 0 },
|
|
55
|
+
unstaged: { files: 0, insertions: 0, deletions: 0 },
|
|
56
|
+
error: errorMessage,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { WorkspaceMetadata } from '../types';
|
|
4
|
+
import { generateMermaidDiagram } from './dependency-analyzer';
|
|
5
|
+
import * as markdownTable from 'markdown-table';
|
|
6
|
+
|
|
7
|
+
export const generateWorkspaceReadme = async (
|
|
8
|
+
metadata: WorkspaceMetadata,
|
|
9
|
+
workspacePath: string
|
|
10
|
+
): Promise<string> => {
|
|
11
|
+
const lines: string[] = [];
|
|
12
|
+
|
|
13
|
+
lines.push(`# ${metadata.workspaceName}`);
|
|
14
|
+
lines.push('');
|
|
15
|
+
|
|
16
|
+
if (metadata.description) {
|
|
17
|
+
lines.push(metadata.description);
|
|
18
|
+
lines.push('');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
lines.push('## Overview');
|
|
22
|
+
lines.push('');
|
|
23
|
+
lines.push(`- **Created:** ${new Date(metadata.createdAt).toLocaleString()}`);
|
|
24
|
+
if (metadata.lastModified) {
|
|
25
|
+
lines.push(`- **Last Modified:** ${new Date(metadata.lastModified).toLocaleString()}`);
|
|
26
|
+
}
|
|
27
|
+
lines.push(`- **Repositories:** ${metadata.repositories.length}`);
|
|
28
|
+
lines.push('');
|
|
29
|
+
|
|
30
|
+
lines.push('## Repositories');
|
|
31
|
+
lines.push('');
|
|
32
|
+
|
|
33
|
+
const tableData: string[][] = [
|
|
34
|
+
['Repository', 'Owner', 'Cloned At', 'Status'],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
for (const repo of metadata.repositories) {
|
|
38
|
+
tableData.push([
|
|
39
|
+
repo.name,
|
|
40
|
+
repo.owner,
|
|
41
|
+
new Date(repo.clonedAt).toLocaleDateString(),
|
|
42
|
+
repo.status === 'success' ? '✓' : '✗',
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Simple table generation without markdown-table for now
|
|
47
|
+
for (const row of tableData) {
|
|
48
|
+
lines.push('| ' + row.join(' | ') + ' |');
|
|
49
|
+
if (tableData.indexOf(row) === 0) {
|
|
50
|
+
lines.push('| ' + row.map(() => '---').join(' | ') + ' |');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
lines.push('');
|
|
54
|
+
|
|
55
|
+
lines.push('## Quick Start');
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push('```bash');
|
|
58
|
+
lines.push(`# Navigate to workspace`);
|
|
59
|
+
lines.push(`wgo ${metadata.workspaceName}`);
|
|
60
|
+
lines.push('');
|
|
61
|
+
lines.push('# Check status');
|
|
62
|
+
lines.push(`workspace status ${metadata.workspaceName}`);
|
|
63
|
+
lines.push('');
|
|
64
|
+
lines.push('# Sync all repositories');
|
|
65
|
+
lines.push(`workspace sync ${metadata.workspaceName}`);
|
|
66
|
+
lines.push('```');
|
|
67
|
+
lines.push('');
|
|
68
|
+
|
|
69
|
+
lines.push('## Commands');
|
|
70
|
+
lines.push('');
|
|
71
|
+
lines.push('- `workspace status` - Check git status across all repos');
|
|
72
|
+
lines.push('- `workspace doctor` - Run health checks');
|
|
73
|
+
lines.push('- `workspace sync` - Pull latest changes');
|
|
74
|
+
lines.push('- `workspace switch-branch` - Switch all repos to a branch');
|
|
75
|
+
lines.push('- `workspace analyze-deps` - Analyze dependencies');
|
|
76
|
+
lines.push('');
|
|
77
|
+
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const generateDependencyGraph = (metadata: WorkspaceMetadata): string => {
|
|
82
|
+
const lines: string[] = [];
|
|
83
|
+
|
|
84
|
+
lines.push('# Dependency Graph');
|
|
85
|
+
lines.push('');
|
|
86
|
+
|
|
87
|
+
if (metadata.dependencies) {
|
|
88
|
+
const analyses = new Map();
|
|
89
|
+
|
|
90
|
+
for (const [repoName, depInfo] of Object.entries(metadata.dependencies)) {
|
|
91
|
+
analyses.set(repoName, {
|
|
92
|
+
repoName,
|
|
93
|
+
dependencies: depInfo.dependsOn,
|
|
94
|
+
dependents: depInfo.dependedBy,
|
|
95
|
+
missingDependencies: [],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const diagram = generateMermaidDiagram(analyses);
|
|
100
|
+
lines.push(diagram);
|
|
101
|
+
} else {
|
|
102
|
+
lines.push('No dependency analysis available. Run `workspace analyze-deps` to generate.');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
lines.push('');
|
|
106
|
+
lines.push('## Dependency Details');
|
|
107
|
+
lines.push('');
|
|
108
|
+
|
|
109
|
+
if (metadata.dependencies) {
|
|
110
|
+
for (const [repoName, depInfo] of Object.entries(metadata.dependencies)) {
|
|
111
|
+
lines.push(`### ${repoName}`);
|
|
112
|
+
lines.push('');
|
|
113
|
+
|
|
114
|
+
if (depInfo.dependsOn.length > 0) {
|
|
115
|
+
lines.push(`**Depends on:** ${depInfo.dependsOn.join(', ')}`);
|
|
116
|
+
lines.push('');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (depInfo.dependedBy.length > 0) {
|
|
120
|
+
lines.push(`**Depended by:** ${depInfo.dependedBy.join(', ')}`);
|
|
121
|
+
lines.push('');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (depInfo.dependsOn.length === 0 && depInfo.dependedBy.length === 0) {
|
|
125
|
+
lines.push('No dependencies');
|
|
126
|
+
lines.push('');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return lines.join('\n');
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const generateRepositoryIndex = async (
|
|
135
|
+
metadata: WorkspaceMetadata,
|
|
136
|
+
workspacePath: string
|
|
137
|
+
): Promise<string> => {
|
|
138
|
+
const lines: string[] = [];
|
|
139
|
+
|
|
140
|
+
lines.push('# Repository Index');
|
|
141
|
+
lines.push('');
|
|
142
|
+
|
|
143
|
+
for (const repo of metadata.repositories) {
|
|
144
|
+
const repoPath = path.join(workspacePath, repo.name);
|
|
145
|
+
|
|
146
|
+
lines.push(`## ${repo.name}`);
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push(`- **Owner:** ${repo.owner}`);
|
|
149
|
+
lines.push(`- **Clone URL:** ${repo.cloneUrl}`);
|
|
150
|
+
lines.push(`- **Cloned At:** ${new Date(repo.clonedAt).toLocaleString()}`);
|
|
151
|
+
|
|
152
|
+
// Try to read package.json for tech stack info
|
|
153
|
+
try {
|
|
154
|
+
const packageJsonPath = path.join(repoPath, 'package.json');
|
|
155
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
156
|
+
|
|
157
|
+
if (packageJson.description) {
|
|
158
|
+
lines.push(`- **Description:** ${packageJson.description}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (packageJson.scripts) {
|
|
162
|
+
const scripts = Object.keys(packageJson.scripts).slice(0, 5);
|
|
163
|
+
lines.push(`- **Scripts:** ${scripts.join(', ')}`);
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// No package.json or can't read
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
lines.push('');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return lines.join('\n');
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const writeDocs = async (
|
|
176
|
+
workspacePath: string,
|
|
177
|
+
metadata: WorkspaceMetadata
|
|
178
|
+
): Promise<void> => {
|
|
179
|
+
const readme = await generateWorkspaceReadme(metadata, workspacePath);
|
|
180
|
+
await fs.writeFile(path.join(workspacePath, 'WORKSPACE-README.md'), readme, 'utf-8');
|
|
181
|
+
|
|
182
|
+
const depGraph = generateDependencyGraph(metadata);
|
|
183
|
+
await fs.writeFile(path.join(workspacePath, 'DEPENDENCY-GRAPH.md'), depGraph, 'utf-8');
|
|
184
|
+
|
|
185
|
+
const repoIndex = await generateRepositoryIndex(metadata, workspacePath);
|
|
186
|
+
await fs.writeFile(path.join(workspacePath, 'REPOSITORY-INDEX.md'), repoIndex, 'utf-8');
|
|
187
|
+
};
|