@chief-clancy/terminal 0.1.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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/hooks/clancy-branch-guard.js +1 -0
- package/dist/hooks/clancy-check-update.js +2 -0
- package/dist/hooks/clancy-context-monitor.js +9 -0
- package/dist/hooks/clancy-credential-guard.js +2 -0
- package/dist/hooks/clancy-drift-detector.js +1 -0
- package/dist/hooks/clancy-notification.js +1 -0
- package/dist/hooks/clancy-post-compact.js +2 -0
- package/dist/hooks/clancy-statusline.js +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-ops/file-ops.d.ts +35 -0
- package/dist/installer/file-ops/file-ops.d.ts.map +1 -0
- package/dist/installer/file-ops/file-ops.js +95 -0
- package/dist/installer/file-ops/file-ops.js.map +1 -0
- package/dist/installer/file-ops/index.d.ts +2 -0
- package/dist/installer/file-ops/index.d.ts.map +1 -0
- package/dist/installer/file-ops/index.js +2 -0
- package/dist/installer/file-ops/index.js.map +1 -0
- package/dist/installer/hook-installer/hook-installer.d.ts +22 -0
- package/dist/installer/hook-installer/hook-installer.d.ts.map +1 -0
- package/dist/installer/hook-installer/hook-installer.js +213 -0
- package/dist/installer/hook-installer/hook-installer.js.map +1 -0
- package/dist/installer/hook-installer/index.d.ts +2 -0
- package/dist/installer/hook-installer/index.d.ts.map +1 -0
- package/dist/installer/hook-installer/index.js +2 -0
- package/dist/installer/hook-installer/index.js.map +1 -0
- package/dist/installer/install/index.d.ts +3 -0
- package/dist/installer/install/index.d.ts.map +1 -0
- package/dist/installer/install/index.js +2 -0
- package/dist/installer/install/index.js.map +1 -0
- package/dist/installer/install/install.d.ts +124 -0
- package/dist/installer/install/install.d.ts.map +1 -0
- package/dist/installer/install/install.js +255 -0
- package/dist/installer/install/install.js.map +1 -0
- package/dist/installer/manifest/index.d.ts +2 -0
- package/dist/installer/manifest/index.d.ts.map +1 -0
- package/dist/installer/manifest/index.js +2 -0
- package/dist/installer/manifest/index.js.map +1 -0
- package/dist/installer/manifest/manifest.d.ts +46 -0
- package/dist/installer/manifest/manifest.d.ts.map +1 -0
- package/dist/installer/manifest/manifest.js +180 -0
- package/dist/installer/manifest/manifest.js.map +1 -0
- package/dist/installer/prompts/index.d.ts +2 -0
- package/dist/installer/prompts/index.d.ts.map +1 -0
- package/dist/installer/prompts/index.js +2 -0
- package/dist/installer/prompts/index.js.map +1 -0
- package/dist/installer/prompts/prompts.d.ts +34 -0
- package/dist/installer/prompts/prompts.d.ts.map +1 -0
- package/dist/installer/prompts/prompts.js +28 -0
- package/dist/installer/prompts/prompts.js.map +1 -0
- package/dist/installer/role-filter/index.d.ts +2 -0
- package/dist/installer/role-filter/index.d.ts.map +1 -0
- package/dist/installer/role-filter/index.js +2 -0
- package/dist/installer/role-filter/index.js.map +1 -0
- package/dist/installer/role-filter/role-filter.d.ts +33 -0
- package/dist/installer/role-filter/role-filter.d.ts.map +1 -0
- package/dist/installer/role-filter/role-filter.js +91 -0
- package/dist/installer/role-filter/role-filter.js.map +1 -0
- package/dist/installer/shared/fs-errors/fs-errors.d.ts +3 -0
- package/dist/installer/shared/fs-errors/fs-errors.d.ts.map +1 -0
- package/dist/installer/shared/fs-errors/fs-errors.js +7 -0
- package/dist/installer/shared/fs-errors/fs-errors.js.map +1 -0
- package/dist/installer/shared/fs-errors/index.d.ts +2 -0
- package/dist/installer/shared/fs-errors/index.d.ts.map +1 -0
- package/dist/installer/shared/fs-errors/index.js +2 -0
- package/dist/installer/shared/fs-errors/index.js.map +1 -0
- package/dist/installer/shared/fs-guards/fs-guards.d.ts +3 -0
- package/dist/installer/shared/fs-guards/fs-guards.d.ts.map +1 -0
- package/dist/installer/shared/fs-guards/fs-guards.js +18 -0
- package/dist/installer/shared/fs-guards/fs-guards.js.map +1 -0
- package/dist/installer/shared/fs-guards/index.d.ts +2 -0
- package/dist/installer/shared/fs-guards/index.d.ts.map +1 -0
- package/dist/installer/shared/fs-guards/index.js +2 -0
- package/dist/installer/shared/fs-guards/index.js.map +1 -0
- package/dist/installer/shared/type-guards/index.d.ts +2 -0
- package/dist/installer/shared/type-guards/index.d.ts.map +1 -0
- package/dist/installer/shared/type-guards/index.js +2 -0
- package/dist/installer/shared/type-guards/index.js.map +1 -0
- package/dist/installer/shared/type-guards/type-guards.d.ts +8 -0
- package/dist/installer/shared/type-guards/type-guards.d.ts.map +1 -0
- package/dist/installer/shared/type-guards/type-guards.js +10 -0
- package/dist/installer/shared/type-guards/type-guards.js.map +1 -0
- package/dist/installer/ui/index.d.ts +2 -0
- package/dist/installer/ui/index.d.ts.map +1 -0
- package/dist/installer/ui/index.js +2 -0
- package/dist/installer/ui/index.js.map +1 -0
- package/dist/installer/ui/ui.d.ts +23 -0
- package/dist/installer/ui/ui.d.ts.map +1 -0
- package/dist/installer/ui/ui.js +121 -0
- package/dist/installer/ui/ui.js.map +1 -0
- package/dist/runner/autopilot/autopilot.d.ts +71 -0
- package/dist/runner/autopilot/autopilot.d.ts.map +1 -0
- package/dist/runner/autopilot/autopilot.js +206 -0
- package/dist/runner/autopilot/autopilot.js.map +1 -0
- package/dist/runner/autopilot/index.d.ts +2 -0
- package/dist/runner/autopilot/index.d.ts.map +1 -0
- package/dist/runner/autopilot/index.js +2 -0
- package/dist/runner/autopilot/index.js.map +1 -0
- package/dist/runner/cli-bridge/cli-bridge.d.ts +34 -0
- package/dist/runner/cli-bridge/cli-bridge.d.ts.map +1 -0
- package/dist/runner/cli-bridge/cli-bridge.js +53 -0
- package/dist/runner/cli-bridge/cli-bridge.js.map +1 -0
- package/dist/runner/cli-bridge/index.d.ts +2 -0
- package/dist/runner/cli-bridge/index.d.ts.map +1 -0
- package/dist/runner/cli-bridge/index.js +2 -0
- package/dist/runner/cli-bridge/index.js.map +1 -0
- package/dist/runner/dep-factory/deliver-phase.d.ts +24 -0
- package/dist/runner/dep-factory/deliver-phase.d.ts.map +1 -0
- package/dist/runner/dep-factory/deliver-phase.js +57 -0
- package/dist/runner/dep-factory/deliver-phase.js.map +1 -0
- package/dist/runner/dep-factory/dep-factory.d.ts +38 -0
- package/dist/runner/dep-factory/dep-factory.d.ts.map +1 -0
- package/dist/runner/dep-factory/dep-factory.js +193 -0
- package/dist/runner/dep-factory/dep-factory.js.map +1 -0
- package/dist/runner/dep-factory/index.d.ts +2 -0
- package/dist/runner/dep-factory/index.d.ts.map +1 -0
- package/dist/runner/dep-factory/index.js +2 -0
- package/dist/runner/dep-factory/index.js.map +1 -0
- package/dist/runner/dep-factory/invoke-phase.d.ts +20 -0
- package/dist/runner/dep-factory/invoke-phase.d.ts.map +1 -0
- package/dist/runner/dep-factory/invoke-phase.js +45 -0
- package/dist/runner/dep-factory/invoke-phase.js.map +1 -0
- package/dist/runner/implement/implement.d.ts +38 -0
- package/dist/runner/implement/implement.d.ts.map +1 -0
- package/dist/runner/implement/implement.js +61 -0
- package/dist/runner/implement/implement.js.map +1 -0
- package/dist/runner/implement/index.d.ts +2 -0
- package/dist/runner/implement/index.d.ts.map +1 -0
- package/dist/runner/implement/index.js +2 -0
- package/dist/runner/implement/index.js.map +1 -0
- package/dist/runner/notify/index.d.ts +2 -0
- package/dist/runner/notify/index.d.ts.map +1 -0
- package/dist/runner/notify/index.js +2 -0
- package/dist/runner/notify/index.js.map +1 -0
- package/dist/runner/notify/notify.d.ts +49 -0
- package/dist/runner/notify/notify.d.ts.map +1 -0
- package/dist/runner/notify/notify.js +90 -0
- package/dist/runner/notify/notify.js.map +1 -0
- package/dist/runner/prompt-builder/index.d.ts +2 -0
- package/dist/runner/prompt-builder/index.d.ts.map +1 -0
- package/dist/runner/prompt-builder/index.js +2 -0
- package/dist/runner/prompt-builder/index.js.map +1 -0
- package/dist/runner/prompt-builder/prompt-builder.d.ts +53 -0
- package/dist/runner/prompt-builder/prompt-builder.d.ts.map +1 -0
- package/dist/runner/prompt-builder/prompt-builder.js +122 -0
- package/dist/runner/prompt-builder/prompt-builder.js.map +1 -0
- package/dist/runner/session-report/index.d.ts +2 -0
- package/dist/runner/session-report/index.d.ts.map +1 -0
- package/dist/runner/session-report/index.js +2 -0
- package/dist/runner/session-report/index.js.map +1 -0
- package/dist/runner/session-report/session-report.d.ts +81 -0
- package/dist/runner/session-report/session-report.d.ts.map +1 -0
- package/dist/runner/session-report/session-report.js +227 -0
- package/dist/runner/session-report/session-report.js.map +1 -0
- package/dist/runner/shared/types.d.ts +30 -0
- package/dist/runner/shared/types.d.ts.map +1 -0
- package/dist/runner/shared/types.js +2 -0
- package/dist/runner/shared/types.js.map +1 -0
- package/dist/shared/ansi/ansi.d.ts +59 -0
- package/dist/shared/ansi/ansi.d.ts.map +1 -0
- package/dist/shared/ansi/ansi.js +59 -0
- package/dist/shared/ansi/ansi.js.map +1 -0
- package/dist/shared/ansi/index.d.ts +2 -0
- package/dist/shared/ansi/index.d.ts.map +1 -0
- package/dist/shared/ansi/index.js +2 -0
- package/dist/shared/ansi/index.js.map +1 -0
- package/package.json +52 -0
- package/src/agents/agents.test.ts +57 -0
- package/src/agents/arch-agent.md +80 -0
- package/src/agents/concerns-agent.md +96 -0
- package/src/agents/design-agent.md +146 -0
- package/src/agents/devils-advocate.md +54 -0
- package/src/agents/quality-agent.md +178 -0
- package/src/agents/tech-agent.md +101 -0
- package/src/agents/verification-gate.md +128 -0
- package/src/roles/implementer/commands/autopilot.md +11 -0
- package/src/roles/implementer/commands/dry-run.md +15 -0
- package/src/roles/implementer/commands/implement.md +19 -0
- package/src/roles/implementer/workflows/autopilot.md +136 -0
- package/src/roles/implementer/workflows/implement.md +161 -0
- package/src/roles/planner/commands/approve-plan.md +11 -0
- package/src/roles/planner/commands/plan.md +22 -0
- package/src/roles/planner/workflows/approve-plan.md +970 -0
- package/src/roles/planner/workflows/plan.md +868 -0
- package/src/roles/reviewer/commands/logs.md +7 -0
- package/src/roles/reviewer/commands/review.md +9 -0
- package/src/roles/reviewer/commands/status.md +9 -0
- package/src/roles/reviewer/workflows/logs.md +109 -0
- package/src/roles/reviewer/workflows/review.md +197 -0
- package/src/roles/reviewer/workflows/status.md +142 -0
- package/src/roles/roles.test.ts +87 -0
- package/src/roles/setup/commands/doctor.md +7 -0
- package/src/roles/setup/commands/help.md +80 -0
- package/src/roles/setup/commands/init.md +7 -0
- package/src/roles/setup/commands/map-codebase.md +17 -0
- package/src/roles/setup/commands/settings.md +7 -0
- package/src/roles/setup/commands/uninstall.md +5 -0
- package/src/roles/setup/commands/update-docs.md +9 -0
- package/src/roles/setup/commands/update.md +13 -0
- package/src/roles/setup/workflows/doctor.md +131 -0
- package/src/roles/setup/workflows/init.md +1096 -0
- package/src/roles/setup/workflows/map-codebase.md +130 -0
- package/src/roles/setup/workflows/scaffold.md +872 -0
- package/src/roles/setup/workflows/settings.md +958 -0
- package/src/roles/setup/workflows/uninstall.md +170 -0
- package/src/roles/setup/workflows/update-docs.md +95 -0
- package/src/roles/setup/workflows/update.md +287 -0
- package/src/roles/strategist/commands/approve-brief.md +23 -0
- package/src/roles/strategist/commands/brief.md +29 -0
- package/src/roles/strategist/workflows/approve-brief.md +1540 -0
- package/src/roles/strategist/workflows/brief.md +1330 -0
- package/src/templates/CLAUDE.md +101 -0
- package/src/templates/templates.test.ts +53 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest-based change detection for installed files.
|
|
3
|
+
*
|
|
4
|
+
* Tracks SHA-256 hashes of installed files so that user modifications can
|
|
5
|
+
* be detected and backed up before an update overwrites them.
|
|
6
|
+
*/
|
|
7
|
+
import { copyFileSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
8
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
9
|
+
import { fileHash } from '../../installer/file-ops/file-ops.js';
|
|
10
|
+
import { hasErrorCode } from '../../installer/shared/fs-errors/index.js';
|
|
11
|
+
import { isPlainObject } from '../../installer/shared/type-guards/index.js';
|
|
12
|
+
/** Check whether a resolved path stays within a base directory. */
|
|
13
|
+
function isInsideBase(base, target) {
|
|
14
|
+
const rel = relative(resolve(base), resolve(target));
|
|
15
|
+
const escapesBase = rel === '..' || rel.startsWith(`..${sep}`);
|
|
16
|
+
return rel !== '' && !escapesBase && !isAbsolute(rel);
|
|
17
|
+
}
|
|
18
|
+
/** Reject paths with traversal segments, backslashes, or absolute prefixes. */
|
|
19
|
+
function isSafeRelativePath(rel) {
|
|
20
|
+
if (rel === '' || rel.includes('\\'))
|
|
21
|
+
return false;
|
|
22
|
+
if (isAbsolute(rel))
|
|
23
|
+
return false;
|
|
24
|
+
const segments = rel.split('/');
|
|
25
|
+
return segments.every((s) => s !== '.' && s !== '..');
|
|
26
|
+
}
|
|
27
|
+
/** Resolve a single directory entry into `[rel, hash]` pairs. */
|
|
28
|
+
function entryToPairs(entry, dir, prefix) {
|
|
29
|
+
if (entry.isSymbolicLink())
|
|
30
|
+
return [];
|
|
31
|
+
const full = join(dir, entry.name);
|
|
32
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
33
|
+
if (entry.isDirectory())
|
|
34
|
+
return collectEntries(full, rel);
|
|
35
|
+
if (!entry.isFile())
|
|
36
|
+
return [];
|
|
37
|
+
return [[rel, fileHash(full)]];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Recursively collect file entries as `[relativePath, hash]` pairs.
|
|
41
|
+
*
|
|
42
|
+
* Pure recursive function — no mutable closure. Skips symlinks and
|
|
43
|
+
* non-file entries (FIFO, socket, etc.) to prevent unexpected behaviour.
|
|
44
|
+
*/
|
|
45
|
+
function collectEntries(dir, prefix) {
|
|
46
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
47
|
+
return entries.flatMap((entry) => entryToPairs(entry, dir, prefix));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build a manifest of installed files with SHA-256 hashes.
|
|
51
|
+
*
|
|
52
|
+
* Recursively walks a directory and records the hash of every file.
|
|
53
|
+
* Symlinks and non-file entries are skipped.
|
|
54
|
+
*
|
|
55
|
+
* @param baseDir - Root directory to scan.
|
|
56
|
+
* @returns A record mapping relative paths to their SHA-256 hashes.
|
|
57
|
+
*/
|
|
58
|
+
export function buildManifest(baseDir) {
|
|
59
|
+
return Object.fromEntries(collectEntries(resolve(baseDir), ''));
|
|
60
|
+
}
|
|
61
|
+
/** Keep only entries where the value is a string. */
|
|
62
|
+
function stringValuesOnly(record) {
|
|
63
|
+
const stringEntries = Object.entries(record).filter((entry) => typeof entry[1] === 'string');
|
|
64
|
+
return Object.fromEntries(stringEntries);
|
|
65
|
+
}
|
|
66
|
+
/** Safely parse manifest JSON, filtering out non-string values. */
|
|
67
|
+
function parseManifestJson(raw) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(raw);
|
|
70
|
+
if (!isPlainObject(parsed))
|
|
71
|
+
return null;
|
|
72
|
+
return stringValuesOnly(parsed);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Try to read and parse a manifest file, returning `null` on any failure. */
|
|
79
|
+
function readManifest(manifestPath) {
|
|
80
|
+
try {
|
|
81
|
+
return parseManifestJson(readFileSync(manifestPath, 'utf8'));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
if (hasErrorCode(err, 'ENOENT'))
|
|
85
|
+
return null;
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Try to hash a file, returning `null` if it no longer exists. */
|
|
90
|
+
function safeFileHash(filePath) {
|
|
91
|
+
try {
|
|
92
|
+
return fileHash(filePath);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (hasErrorCode(err, 'ENOENT'))
|
|
96
|
+
return null;
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Check whether a manifest entry has been modified on disk. */
|
|
101
|
+
function isModified(baseDir, rel, hash) {
|
|
102
|
+
if (!isSafeRelativePath(rel))
|
|
103
|
+
return false;
|
|
104
|
+
const absPath = join(baseDir, rel);
|
|
105
|
+
if (!isInsideBase(baseDir, absPath))
|
|
106
|
+
return false;
|
|
107
|
+
const currentHash = safeFileHash(absPath);
|
|
108
|
+
return currentHash !== null && currentHash !== hash;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Detect files modified by the user since last install.
|
|
112
|
+
*
|
|
113
|
+
* Compares current file hashes against the stored manifest to find changes.
|
|
114
|
+
* Manifest keys containing path traversal sequences are rejected.
|
|
115
|
+
*
|
|
116
|
+
* @param baseDir - The installed directory to check.
|
|
117
|
+
* @param manifestPath - Path to the stored manifest JSON.
|
|
118
|
+
* @returns Array of modified file records with relative and absolute paths.
|
|
119
|
+
*/
|
|
120
|
+
export function detectModifiedFiles(baseDir, manifestPath) {
|
|
121
|
+
const manifest = readManifest(manifestPath);
|
|
122
|
+
if (manifest === null)
|
|
123
|
+
return [];
|
|
124
|
+
const resolvedBase = resolve(baseDir);
|
|
125
|
+
return Object.entries(manifest)
|
|
126
|
+
.filter(([rel, hash]) => isModified(resolvedBase, rel, hash))
|
|
127
|
+
.map(([rel]) => ({ rel, absPath: join(resolvedBase, rel) }));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Try to copy a file, silently skipping if it no longer exists.
|
|
131
|
+
*
|
|
132
|
+
* @returns `true` if the copy succeeded, `false` if the source was missing.
|
|
133
|
+
*/
|
|
134
|
+
function safeCopy(src, dest) {
|
|
135
|
+
try {
|
|
136
|
+
copyFileSync(src, dest);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
if (hasErrorCode(err, 'ENOENT'))
|
|
141
|
+
return false;
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Copy a single modified file into the patches directory, returning its rel path on success. */
|
|
146
|
+
function copyToPatches(entry, patchesDir) {
|
|
147
|
+
if (!isSafeRelativePath(entry.rel))
|
|
148
|
+
return [];
|
|
149
|
+
const backupPath = join(patchesDir, entry.rel);
|
|
150
|
+
if (!isInsideBase(patchesDir, backupPath))
|
|
151
|
+
return [];
|
|
152
|
+
mkdirSync(dirname(backupPath), { recursive: true });
|
|
153
|
+
return safeCopy(entry.absPath, backupPath) ? [entry.rel] : [];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Back up modified files to a patches directory.
|
|
157
|
+
*
|
|
158
|
+
* Copies each modified file and writes a `backup-meta.json` with metadata.
|
|
159
|
+
* Files that vanish between detection and backup are silently skipped.
|
|
160
|
+
* Paths that escape the patches directory are rejected.
|
|
161
|
+
*
|
|
162
|
+
* @param modified - Array of modified file records.
|
|
163
|
+
* @param patchesDir - Directory to store backups.
|
|
164
|
+
* @returns The patches directory path, or `null` if no files were backed up.
|
|
165
|
+
*/
|
|
166
|
+
export function backupModifiedFiles(modified, patchesDir) {
|
|
167
|
+
if (modified.length === 0)
|
|
168
|
+
return null;
|
|
169
|
+
mkdirSync(patchesDir, { recursive: true });
|
|
170
|
+
const copiedPaths = modified.flatMap((entry) => copyToPatches(entry, patchesDir));
|
|
171
|
+
if (copiedPaths.length === 0)
|
|
172
|
+
return null;
|
|
173
|
+
const backupMeta = {
|
|
174
|
+
backed_up: copiedPaths,
|
|
175
|
+
date: new Date().toISOString(),
|
|
176
|
+
};
|
|
177
|
+
writeFileSync(join(patchesDir, 'backup-meta.json'), JSON.stringify(backupMeta, null, 2));
|
|
178
|
+
return patchesDir;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/installer/manifest/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/installer/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/installer/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI prompt helpers for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Uses dependency injection for the readline interface — no module-scoped
|
|
5
|
+
* side effects. The caller creates and owns the readline lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Minimal readline interface required by the prompts module.
|
|
9
|
+
*
|
|
10
|
+
* Accepts any object with `question` and `close` — works with
|
|
11
|
+
* `node:readline` createInterface or a test double.
|
|
12
|
+
*/
|
|
13
|
+
type ReadlineInterface = {
|
|
14
|
+
readonly question: (query: string, callback: (answer: string) => void) => void;
|
|
15
|
+
readonly close: () => void;
|
|
16
|
+
};
|
|
17
|
+
/** Public API returned by {@link createPrompts}. */
|
|
18
|
+
type PromptApi = {
|
|
19
|
+
/** Prompt the user for text input. */
|
|
20
|
+
readonly ask: (label: string) => Promise<string>;
|
|
21
|
+
/** Present a numbered list of options and return the user's choice. */
|
|
22
|
+
readonly choose: (question: string, options: readonly string[], defaultChoice?: number) => Promise<string>;
|
|
23
|
+
/** Close the underlying readline interface. */
|
|
24
|
+
readonly close: () => void;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Create a prompt API backed by the given readline interface.
|
|
28
|
+
*
|
|
29
|
+
* @param rl - A readline-compatible interface (injected for testability).
|
|
30
|
+
* @returns An object with `ask`, `choose`, and `close` methods.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createPrompts(rl: ReadlineInterface): PromptApi;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/installer/prompts/prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;GAKG;AACH,KAAK,iBAAiB,GAAG;IACvB,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,KAC/B,IAAI,CAAC;IACV,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF,oDAAoD;AACpD,KAAK,SAAS,GAAG;IACf,sCAAsC;IACtC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,aAAa,CAAC,EAAE,MAAM,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAqB9D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI prompt helpers for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Uses dependency injection for the readline interface — no module-scoped
|
|
5
|
+
* side effects. The caller creates and owns the readline lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
import { blue, cyan } from '../../shared/ansi/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create a prompt API backed by the given readline interface.
|
|
10
|
+
*
|
|
11
|
+
* @param rl - A readline-compatible interface (injected for testability).
|
|
12
|
+
* @returns An object with `ask`, `choose`, and `close` methods.
|
|
13
|
+
*/
|
|
14
|
+
export function createPrompts(rl) {
|
|
15
|
+
const ask = (label) => new Promise((resolve) => rl.question(label, resolve));
|
|
16
|
+
const choose = async (question, options, defaultChoice = 1) => {
|
|
17
|
+
console.log('');
|
|
18
|
+
console.log(blue(question));
|
|
19
|
+
console.log('');
|
|
20
|
+
options.forEach((opt, i) => console.log(` ${i + 1}) ${opt}`));
|
|
21
|
+
console.log('');
|
|
22
|
+
const raw = await ask(cyan(`Choice [${defaultChoice}]: `));
|
|
23
|
+
return raw.trim() || String(defaultChoice);
|
|
24
|
+
};
|
|
25
|
+
const close = () => rl.close();
|
|
26
|
+
return { ask, choose, close };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/installer/prompts/prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AA8BtD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAqB;IACjD,MAAM,GAAG,GAAG,CAAC,KAAa,EAAmB,EAAE,CAC7C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,KAAK,EAClB,QAAgB,EAChB,OAA0B,EAC1B,aAAa,GAAG,CAAC,EACA,EAAE;QACnB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,aAAa,KAAK,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAErC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/installer/role-filter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/installer/role-filter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Options for copying role files. */
|
|
2
|
+
type CopyRoleFilesOptions = {
|
|
3
|
+
/** The roles source directory (e.g. `src/roles/`). */
|
|
4
|
+
readonly rolesDir: string;
|
|
5
|
+
/** The subdirectory within each role (`commands` or `workflows`). */
|
|
6
|
+
readonly subdir: string;
|
|
7
|
+
/** The shared destination directory (all roles merge into this). */
|
|
8
|
+
readonly dest: string;
|
|
9
|
+
/** Set of enabled optional roles, or `null` to install all (first install). */
|
|
10
|
+
readonly enabledRoles: ReadonlySet<string> | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Copy files from role subdirectories into a shared destination directory.
|
|
14
|
+
*
|
|
15
|
+
* Walks `rolesDir/{role}/{subdir}/` for each role and merges contents
|
|
16
|
+
* into `dest` (no per-role subdirectories). Core roles (implementer, reviewer, setup) are always
|
|
17
|
+
* copied. Optional roles are only copied if listed in `enabledRoles`,
|
|
18
|
+
* or if `enabledRoles` is `null` (first install — install all).
|
|
19
|
+
*
|
|
20
|
+
* Files for disabled optional roles are removed from the destination
|
|
21
|
+
* based on the current source tree. If a role's subdir is missing,
|
|
22
|
+
* no cleanup occurs (we can't know what to remove).
|
|
23
|
+
*
|
|
24
|
+
* Filenames must be unique across all roles — if two roles define the same
|
|
25
|
+
* filename, the last one copied wins silently. This is enforced by convention
|
|
26
|
+
* in the role directory structure, not at runtime.
|
|
27
|
+
*
|
|
28
|
+
* @param options - The role filter options.
|
|
29
|
+
* @returns Nothing — files are copied/removed in place.
|
|
30
|
+
*/
|
|
31
|
+
export declare function copyRoleFiles(options: CopyRoleFilesOptions): void;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=role-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-filter.d.ts","sourceRoot":"","sources":["../../../src/installer/role-filter/role-filter.ts"],"names":[],"mappings":"AAgBA,sCAAsC;AACtC,KAAK,oBAAoB,GAAG;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACnD,CAAC;AAgDF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAqBjE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-based file filtering for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Copies command/workflow files from role directories into a shared destination,
|
|
5
|
+
* applying core vs optional role filtering. Disabled optional roles have their
|
|
6
|
+
* previously-installed files removed.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdirSync, readdirSync, rmSync, unlinkSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { copyDir } from '../../installer/file-ops/file-ops.js';
|
|
11
|
+
import { hasErrorCode } from '../../installer/shared/fs-errors/index.js';
|
|
12
|
+
/** Roles that are always installed regardless of CLANCY_ROLES. */
|
|
13
|
+
const CORE_ROLES = new Set(['implementer', 'reviewer', 'setup']);
|
|
14
|
+
/** Check whether a role should be installed. */
|
|
15
|
+
function shouldInstallRole(roleName, enabledRoles) {
|
|
16
|
+
if (CORE_ROLES.has(roleName))
|
|
17
|
+
return true;
|
|
18
|
+
return enabledRoles === null || enabledRoles.has(roleName);
|
|
19
|
+
}
|
|
20
|
+
/** Silently unlink a file, ignoring ENOENT if it no longer exists. */
|
|
21
|
+
function safeUnlink(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
unlinkSync(filePath);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (!hasErrorCode(err, 'ENOENT'))
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Remove previously-installed files and subdirectories for a disabled role. */
|
|
31
|
+
function cleanDisabledFiles(srcDir, dest) {
|
|
32
|
+
readdirSync(srcDir, { withFileTypes: true }).forEach((entry) => {
|
|
33
|
+
const target = join(dest, entry.name);
|
|
34
|
+
if (entry.isFile()) {
|
|
35
|
+
safeUnlink(target);
|
|
36
|
+
}
|
|
37
|
+
else if (entry.isDirectory()) {
|
|
38
|
+
rmSync(target, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Try to read a directory, returning null if it doesn't exist or isn't a directory. */
|
|
43
|
+
function safeReaddir(dir) {
|
|
44
|
+
try {
|
|
45
|
+
return readdirSync(dir);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const isMissingDir = hasErrorCode(err, 'ENOENT');
|
|
49
|
+
const isNotDirectory = hasErrorCode(err, 'ENOTDIR');
|
|
50
|
+
if (isMissingDir || isNotDirectory)
|
|
51
|
+
return null;
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Copy files from role subdirectories into a shared destination directory.
|
|
57
|
+
*
|
|
58
|
+
* Walks `rolesDir/{role}/{subdir}/` for each role and merges contents
|
|
59
|
+
* into `dest` (no per-role subdirectories). Core roles (implementer, reviewer, setup) are always
|
|
60
|
+
* copied. Optional roles are only copied if listed in `enabledRoles`,
|
|
61
|
+
* or if `enabledRoles` is `null` (first install — install all).
|
|
62
|
+
*
|
|
63
|
+
* Files for disabled optional roles are removed from the destination
|
|
64
|
+
* based on the current source tree. If a role's subdir is missing,
|
|
65
|
+
* no cleanup occurs (we can't know what to remove).
|
|
66
|
+
*
|
|
67
|
+
* Filenames must be unique across all roles — if two roles define the same
|
|
68
|
+
* filename, the last one copied wins silently. This is enforced by convention
|
|
69
|
+
* in the role directory structure, not at runtime.
|
|
70
|
+
*
|
|
71
|
+
* @param options - The role filter options.
|
|
72
|
+
* @returns Nothing — files are copied/removed in place.
|
|
73
|
+
*/
|
|
74
|
+
export function copyRoleFiles(options) {
|
|
75
|
+
const { rolesDir, subdir, dest, enabledRoles } = options;
|
|
76
|
+
mkdirSync(dest, { recursive: true });
|
|
77
|
+
const roles = readdirSync(rolesDir, { withFileTypes: true })
|
|
78
|
+
.filter((d) => d.isDirectory())
|
|
79
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
80
|
+
roles.forEach((role) => {
|
|
81
|
+
const srcDir = join(rolesDir, role.name, subdir);
|
|
82
|
+
if (safeReaddir(srcDir) === null)
|
|
83
|
+
return;
|
|
84
|
+
if (shouldInstallRole(role.name, enabledRoles)) {
|
|
85
|
+
copyDir(srcDir, dest);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
cleanDisabledFiles(srcDir, dest);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=role-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-filter.js","sourceRoot":"","sources":["../../../src/installer/role-filter/role-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-errors.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/fs-errors/fs-errors.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKhE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Check whether an error has a specific Node.js error code. */
|
|
2
|
+
export function hasErrorCode(err, code) {
|
|
3
|
+
return (
|
|
4
|
+
// Safe: instanceof Error guarantees err is an object; code is a Node.js extension
|
|
5
|
+
err instanceof Error && err.code === code);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=fs-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-errors.js","sourceRoot":"","sources":["../../../../src/installer/shared/fs-errors/fs-errors.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,GAAY,EAAE,IAAY;IACrD,OAAO;IACL,kFAAkF;IAClF,GAAG,YAAY,KAAK,IAAK,GAAkC,CAAC,IAAI,KAAK,IAAI,CAC1E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/fs-errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/installer/shared/fs-errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-guards.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/fs-guards/fs-guards.ts"],"names":[],"mappings":"AAOA,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQhD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem safety guards shared across installer modules.
|
|
3
|
+
*/
|
|
4
|
+
import { lstatSync } from 'node:fs';
|
|
5
|
+
import { hasErrorCode } from '../fs-errors/fs-errors.js';
|
|
6
|
+
/** Throw if the given path is a symlink. Only swallows ENOENT. */
|
|
7
|
+
export function rejectSymlink(path) {
|
|
8
|
+
try {
|
|
9
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
10
|
+
throw new Error(`${path} is a symlink. Remove it before installing.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
if (!hasErrorCode(err, 'ENOENT'))
|
|
15
|
+
throw err;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=fs-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-guards.js","sourceRoot":"","sources":["../../../../src/installer/shared/fs-guards/fs-guards.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6CAA6C,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC;YAAE,MAAM,GAAG,CAAC;IAC9C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/fs-guards/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/installer/shared/fs-guards/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/type-guards/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/installer/shared/type-guards/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a value is a plain object (not array, not null).
|
|
3
|
+
*
|
|
4
|
+
* Intentionally duplicated in `hooks/shared/types` — hooks are
|
|
5
|
+
* self-contained esbuild bundles and cannot share code with the installer.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
8
|
+
//# sourceMappingURL=type-guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["../../../../src/installer/shared/type-guards/type-guards.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a value is a plain object (not array, not null).
|
|
3
|
+
*
|
|
4
|
+
* Intentionally duplicated in `hooks/shared/types` — hooks are
|
|
5
|
+
* self-contained esbuild bundles and cannot share code with the installer.
|
|
6
|
+
*/
|
|
7
|
+
export function isPlainObject(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=type-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.js","sourceRoot":"","sources":["../../../../src/installer/shared/type-guards/type-guards.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAc;IAEd,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/installer/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/installer/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer UI — banner and success message.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Print the Clancy ASCII banner and version info.
|
|
6
|
+
*
|
|
7
|
+
* @param version - The package version string to display.
|
|
8
|
+
* @returns Nothing — output is written to stdout.
|
|
9
|
+
*/
|
|
10
|
+
export declare function printBanner(version: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Print the post-install success message with available commands.
|
|
13
|
+
*
|
|
14
|
+
* Command groups with a non-null `roleKey` are role-gated: they are shown only
|
|
15
|
+
* when their role key is present in `enabledRoles`. Groups with `roleKey === null`
|
|
16
|
+
* are always shown. Pass `null` for `enabledRoles` to disable role gating and show
|
|
17
|
+
* all groups.
|
|
18
|
+
*
|
|
19
|
+
* @param enabledRoles - Role keys matching `CommandGroup.roleKey` (e.g. `"strategist"`), or `null` to show all groups.
|
|
20
|
+
* @returns Nothing — output is written to stdout.
|
|
21
|
+
*/
|
|
22
|
+
export declare function printSuccess(enabledRoles: ReadonlySet<string> | null): void;
|
|
23
|
+
//# sourceMappingURL=ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/installer/ui/ui.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2EH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAYjD;AA2BD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAgB3E"}
|