@hacksmith/doraval 0.2.35 → 0.2.42
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/doraval.js +1367 -453
- package/bin/ui/index.html +284 -0
- package/package.json +2 -2
package/bin/doraval.js
CHANGED
|
@@ -599,7 +599,7 @@ var init_dist = __esm(() => {
|
|
|
599
599
|
var require_package = __commonJS((exports, module) => {
|
|
600
600
|
module.exports = {
|
|
601
601
|
name: "@hacksmith/doraval",
|
|
602
|
-
version: "0.2.
|
|
602
|
+
version: "0.2.42",
|
|
603
603
|
author: "Saif",
|
|
604
604
|
repository: {
|
|
605
605
|
type: "git",
|
|
@@ -640,7 +640,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
640
640
|
"apps/*"
|
|
641
641
|
],
|
|
642
642
|
scripts: {
|
|
643
|
-
build: "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun",
|
|
643
|
+
build: "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun && rm -rf bin/ui && cp -r src/ui bin/ui",
|
|
644
644
|
dev: "bun run ./src/cli/index.ts",
|
|
645
645
|
test: "bun test",
|
|
646
646
|
typecheck: "bunx tsc --noEmit --skipLibCheck",
|
|
@@ -906,7 +906,7 @@ var init_validate = __esm(() => {
|
|
|
906
906
|
validate_default = defineCommand({
|
|
907
907
|
meta: {
|
|
908
908
|
name: "validate",
|
|
909
|
-
description: "Validate structure and schema of a skill or plugin"
|
|
909
|
+
description: "Validate structure and schema of a skill or plugin (keywords in plugin.json help agent discovery)"
|
|
910
910
|
},
|
|
911
911
|
args: {
|
|
912
912
|
path: {
|
|
@@ -1817,20 +1817,483 @@ var init_list = __esm(() => {
|
|
|
1817
1817
|
});
|
|
1818
1818
|
});
|
|
1819
1819
|
|
|
1820
|
+
// src/cli/commands/journal/context.ts
|
|
1821
|
+
var exports_context = {};
|
|
1822
|
+
__export(exports_context, {
|
|
1823
|
+
generateJournalContext: () => generateJournalContext,
|
|
1824
|
+
default: () => context_default
|
|
1825
|
+
});
|
|
1826
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1827
|
+
import { join as join4, resolve as resolvePath } from "path";
|
|
1828
|
+
function truncate(text, max = 180) {
|
|
1829
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
1830
|
+
if (clean.length <= max)
|
|
1831
|
+
return clean;
|
|
1832
|
+
return clean.slice(0, max - 1) + "\u2026";
|
|
1833
|
+
}
|
|
1834
|
+
function groupByPushback(entries) {
|
|
1835
|
+
const strong = [];
|
|
1836
|
+
const friction = [];
|
|
1837
|
+
const nudge = [];
|
|
1838
|
+
for (const e of entries) {
|
|
1839
|
+
const pb = e.pushback ?? 0;
|
|
1840
|
+
if (pb >= 7)
|
|
1841
|
+
strong.push(e);
|
|
1842
|
+
else if (pb >= 4)
|
|
1843
|
+
friction.push(e);
|
|
1844
|
+
else
|
|
1845
|
+
nudge.push(e);
|
|
1846
|
+
}
|
|
1847
|
+
const byPush = (a, b) => (b.pushback ?? 0) - (a.pushback ?? 0);
|
|
1848
|
+
return {
|
|
1849
|
+
strong: strong.sort(byPush),
|
|
1850
|
+
friction: friction.sort(byPush),
|
|
1851
|
+
nudge: nudge.sort(byPush)
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
function generateJournalContext(entries, project, opts = {}) {
|
|
1855
|
+
const minPb = opts.minPushback ?? 1;
|
|
1856
|
+
const showFull = !!opts.full;
|
|
1857
|
+
const includeGlobal = !opts.noGlobal;
|
|
1858
|
+
let filtered = entries.filter((e) => {
|
|
1859
|
+
const pb = e.pushback ?? 0;
|
|
1860
|
+
const isActive = (e.status || "active") === "active";
|
|
1861
|
+
return isActive && pb >= minPb;
|
|
1862
|
+
});
|
|
1863
|
+
if (!includeGlobal) {}
|
|
1864
|
+
if (filtered.length === 0) {
|
|
1865
|
+
return "";
|
|
1866
|
+
}
|
|
1867
|
+
const groups = groupByPushback(filtered);
|
|
1868
|
+
let out = "";
|
|
1869
|
+
const scope = project ? `for ${project}` : "from your journal";
|
|
1870
|
+
out += `These are recorded decisions and principles ${scope}. `;
|
|
1871
|
+
out += `Higher pushback entries represent stronger prior commitments.
|
|
1872
|
+
|
|
1873
|
+
`;
|
|
1874
|
+
const renderGroup = (title, items) => {
|
|
1875
|
+
if (items.length === 0)
|
|
1876
|
+
return "";
|
|
1877
|
+
let g = `## ${title}
|
|
1878
|
+
|
|
1879
|
+
`;
|
|
1880
|
+
for (const e of items) {
|
|
1881
|
+
const tags = (e.tags || []).length ? `tags: ${(e.tags || []).join(", ")}` : "";
|
|
1882
|
+
const pb = e.pushback ?? 0;
|
|
1883
|
+
g += `- **${e.title}** (pushback: ${pb}${tags ? `, ${tags}` : ""})
|
|
1884
|
+
`;
|
|
1885
|
+
const body = showFull ? (e.rationale || "").trim() : truncate(e.rationale || "");
|
|
1886
|
+
if (body) {
|
|
1887
|
+
g += ` ${body}
|
|
1888
|
+
`;
|
|
1889
|
+
}
|
|
1890
|
+
g += `
|
|
1891
|
+
`;
|
|
1892
|
+
}
|
|
1893
|
+
return g;
|
|
1894
|
+
};
|
|
1895
|
+
out += renderGroup("Strong (pushback 7\u201310)", groups.strong);
|
|
1896
|
+
out += renderGroup("Friction (pushback 4\u20136)", groups.friction);
|
|
1897
|
+
out += renderGroup("Nudges (pushback 1\u20133)", groups.nudge);
|
|
1898
|
+
out += "If these feel out of date, run: `dora journal update` or `dora journal list`.\n";
|
|
1899
|
+
return out.trimEnd() + `
|
|
1900
|
+
`;
|
|
1901
|
+
}
|
|
1902
|
+
async function appendOrUpdateJournalBlock(target, contextText, project, useReference) {
|
|
1903
|
+
const absTarget = resolvePath(process.cwd(), target);
|
|
1904
|
+
let original = "";
|
|
1905
|
+
if (existsSync5(absTarget)) {
|
|
1906
|
+
original = await Bun.file(absTarget).text();
|
|
1907
|
+
}
|
|
1908
|
+
let blockContent;
|
|
1909
|
+
if (useReference) {
|
|
1910
|
+
const refLines = [];
|
|
1911
|
+
refLines.push("## Recorded decisions (from journal)");
|
|
1912
|
+
refLines.push("");
|
|
1913
|
+
refLines.push("@~/.doraval/journals/global.md");
|
|
1914
|
+
if (project) {
|
|
1915
|
+
refLines.push(`@~/.doraval/journals/${project}.md`);
|
|
1916
|
+
}
|
|
1917
|
+
refLines.push("");
|
|
1918
|
+
refLines.push("_These are your synced project decisions. High pushback items are strong commitments._");
|
|
1919
|
+
blockContent = refLines.join(`
|
|
1920
|
+
`);
|
|
1921
|
+
} else {
|
|
1922
|
+
blockContent = contextText.trim();
|
|
1923
|
+
}
|
|
1924
|
+
const newBlock = [
|
|
1925
|
+
JOURNAL_BLOCK_START,
|
|
1926
|
+
"",
|
|
1927
|
+
blockContent,
|
|
1928
|
+
"",
|
|
1929
|
+
JOURNAL_BLOCK_END
|
|
1930
|
+
].join(`
|
|
1931
|
+
`);
|
|
1932
|
+
let updated;
|
|
1933
|
+
const startIdx = original.indexOf(JOURNAL_BLOCK_START);
|
|
1934
|
+
const endIdx = original.indexOf(JOURNAL_BLOCK_END);
|
|
1935
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
1936
|
+
const before = original.slice(0, startIdx);
|
|
1937
|
+
const after = original.slice(endIdx + JOURNAL_BLOCK_END.length);
|
|
1938
|
+
updated = before + newBlock + after;
|
|
1939
|
+
} else {
|
|
1940
|
+
const separator = original.trim().length > 0 ? `
|
|
1941
|
+
|
|
1942
|
+
` : "";
|
|
1943
|
+
updated = original + separator + newBlock + `
|
|
1944
|
+
`;
|
|
1945
|
+
}
|
|
1946
|
+
await Bun.write(absTarget, updated);
|
|
1947
|
+
const action = existsSync5(absTarget) && startIdx !== -1 ? "Updated" : "Added";
|
|
1948
|
+
ui.write(`
|
|
1949
|
+
${import_picocolors7.default.green("\u2713")} ${action} journal decisions section in ${import_picocolors7.default.white(target)}`);
|
|
1950
|
+
if (useReference) {
|
|
1951
|
+
ui.write(` ${import_picocolors7.default.dim("Using @import references (full files will be loaded by Claude).")}`);
|
|
1952
|
+
} else {
|
|
1953
|
+
ui.write(` ${import_picocolors7.default.dim("Embedded compact decisions (low noise).")}`);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
var import_picocolors7, JOURNAL_BLOCK_START = "<!-- doraval-journal:start -->", JOURNAL_BLOCK_END = "<!-- doraval-journal:end -->", context_default;
|
|
1957
|
+
var init_context = __esm(() => {
|
|
1958
|
+
init_dist();
|
|
1959
|
+
init_out();
|
|
1960
|
+
init_journal_config();
|
|
1961
|
+
init_journal_parse();
|
|
1962
|
+
import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
1963
|
+
context_default = defineCommand({
|
|
1964
|
+
meta: {
|
|
1965
|
+
name: "context",
|
|
1966
|
+
description: "Output compact journal decisions (for hooks, CLAUDE.md, or piping)"
|
|
1967
|
+
},
|
|
1968
|
+
args: {
|
|
1969
|
+
project: {
|
|
1970
|
+
type: "string",
|
|
1971
|
+
alias: "p",
|
|
1972
|
+
description: "Project name (defaults to directory-based mapping)"
|
|
1973
|
+
},
|
|
1974
|
+
"min-pushback": {
|
|
1975
|
+
type: "string",
|
|
1976
|
+
description: "Only include entries with at least this pushback (1-10)",
|
|
1977
|
+
default: "1"
|
|
1978
|
+
},
|
|
1979
|
+
full: {
|
|
1980
|
+
type: "boolean",
|
|
1981
|
+
description: "Show full rationale instead of truncated",
|
|
1982
|
+
default: false
|
|
1983
|
+
},
|
|
1984
|
+
"no-global": {
|
|
1985
|
+
type: "boolean",
|
|
1986
|
+
description: "Exclude global entries (project only)",
|
|
1987
|
+
default: false
|
|
1988
|
+
},
|
|
1989
|
+
"append-to": {
|
|
1990
|
+
type: "string",
|
|
1991
|
+
description: "Append (or update) a managed section in this file (e.g. CLAUDE.md or AGENTS.md)"
|
|
1992
|
+
},
|
|
1993
|
+
reference: {
|
|
1994
|
+
type: "boolean",
|
|
1995
|
+
description: "When appending, use @import references instead of embedding compact decisions",
|
|
1996
|
+
default: false
|
|
1997
|
+
},
|
|
1998
|
+
"print-hook": {
|
|
1999
|
+
type: "boolean",
|
|
2000
|
+
description: "Print a ready-to-paste SessionStart hook snippet for hooks.json",
|
|
2001
|
+
default: false
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
2004
|
+
async run({ args }) {
|
|
2005
|
+
if (args["print-hook"]) {
|
|
2006
|
+
const hookCmd = "sh -c 'dora journal context 2>/dev/null || true'";
|
|
2007
|
+
console.log(JSON.stringify({
|
|
2008
|
+
SessionStart: [
|
|
2009
|
+
{
|
|
2010
|
+
hooks: [
|
|
2011
|
+
{
|
|
2012
|
+
type: "command",
|
|
2013
|
+
command: hookCmd
|
|
2014
|
+
}
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
]
|
|
2018
|
+
}, null, 2));
|
|
2019
|
+
console.error("\nTip: Use `dora journal hook enable` to install the hook automatically.");
|
|
2020
|
+
console.error(" Use `dora journal hook disable` to remove it.");
|
|
2021
|
+
console.error(" (sh -c wrapper ensures shell features like redir work reliably.)");
|
|
2022
|
+
process.exit(0);
|
|
2023
|
+
}
|
|
2024
|
+
const config = await readConfig();
|
|
2025
|
+
let project = args.project;
|
|
2026
|
+
if (!project) {
|
|
2027
|
+
project = resolveProjectName(config) ?? undefined;
|
|
2028
|
+
}
|
|
2029
|
+
if (project) {
|
|
2030
|
+
project = sanitizeProjectName(project);
|
|
2031
|
+
}
|
|
2032
|
+
const journalsDir = getJournalsDir();
|
|
2033
|
+
const entries = [];
|
|
2034
|
+
const globalPath = join4(journalsDir, "global.md");
|
|
2035
|
+
if (existsSync5(globalPath)) {
|
|
2036
|
+
try {
|
|
2037
|
+
const raw = await Bun.file(globalPath).text();
|
|
2038
|
+
entries.push(...parseJournalEntries(raw));
|
|
2039
|
+
} catch {}
|
|
2040
|
+
}
|
|
2041
|
+
if (project) {
|
|
2042
|
+
const projectPath = join4(journalsDir, `${project}.md`);
|
|
2043
|
+
if (existsSync5(projectPath)) {
|
|
2044
|
+
try {
|
|
2045
|
+
const raw = await Bun.file(projectPath).text();
|
|
2046
|
+
entries.push(...parseJournalEntries(raw));
|
|
2047
|
+
} catch {}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
const minPb = parseInt(String(args["min-pushback"] ?? "1"), 10) || 1;
|
|
2051
|
+
const contextText = generateJournalContext(entries, project ?? null, {
|
|
2052
|
+
minPushback: minPb,
|
|
2053
|
+
full: !!args.full,
|
|
2054
|
+
noGlobal: !!args["no-global"]
|
|
2055
|
+
});
|
|
2056
|
+
const appendTarget = args["append-to"];
|
|
2057
|
+
if (appendTarget && contextText) {
|
|
2058
|
+
await appendOrUpdateJournalBlock(appendTarget, contextText, project ?? null, !!args.reference);
|
|
2059
|
+
}
|
|
2060
|
+
if (contextText && !appendTarget) {
|
|
2061
|
+
console.log(contextText);
|
|
2062
|
+
} else if (contextText && appendTarget) {}
|
|
2063
|
+
process.exit(0);
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
// src/cli/commands/journal/hook.ts
|
|
2069
|
+
var exports_hook = {};
|
|
2070
|
+
__export(exports_hook, {
|
|
2071
|
+
writeHookConfig: () => writeJson,
|
|
2072
|
+
removeHook: () => removeHook,
|
|
2073
|
+
readHookConfig: () => readJson,
|
|
2074
|
+
hasHook: () => hasHook,
|
|
2075
|
+
getLocalHooksPath: () => getLocalHooksPath,
|
|
2076
|
+
getGlobalSettingsPath: () => getGlobalSettingsPath,
|
|
2077
|
+
default: () => hook_default,
|
|
2078
|
+
addHook: () => addHook
|
|
2079
|
+
});
|
|
2080
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync, rmdirSync, readdirSync as readdirSync2 } from "fs";
|
|
2081
|
+
import { join as join5, dirname } from "path";
|
|
2082
|
+
import { homedir as homedir2 } from "os";
|
|
2083
|
+
function getGlobalSettingsPath() {
|
|
2084
|
+
return join5(homedir2(), ".claude", "settings.json");
|
|
2085
|
+
}
|
|
2086
|
+
function getLocalHooksPath() {
|
|
2087
|
+
return join5(process.cwd(), "hooks", "hooks.json");
|
|
2088
|
+
}
|
|
2089
|
+
async function readJson(file) {
|
|
2090
|
+
if (!existsSync6(file))
|
|
2091
|
+
return {};
|
|
2092
|
+
try {
|
|
2093
|
+
const raw = await Bun.file(file).text();
|
|
2094
|
+
return JSON.parse(raw);
|
|
2095
|
+
} catch {
|
|
2096
|
+
return {};
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
async function writeJson(file, data) {
|
|
2100
|
+
const dir = dirname(file);
|
|
2101
|
+
if (!existsSync6(dir)) {
|
|
2102
|
+
mkdirSync2(dir, { recursive: true });
|
|
2103
|
+
}
|
|
2104
|
+
await Bun.write(file, JSON.stringify(data, null, 2) + `
|
|
2105
|
+
`);
|
|
2106
|
+
}
|
|
2107
|
+
function hasHook(config) {
|
|
2108
|
+
const sessionStart = config?.hooks?.SessionStart;
|
|
2109
|
+
if (!Array.isArray(sessionStart))
|
|
2110
|
+
return false;
|
|
2111
|
+
return sessionStart.some((group) => Array.isArray(group?.hooks) && group.hooks.some((h) => h?.command === HOOK_COMMAND));
|
|
2112
|
+
}
|
|
2113
|
+
async function addHook(file) {
|
|
2114
|
+
const original = await readJson(file);
|
|
2115
|
+
const config = JSON.parse(JSON.stringify(original));
|
|
2116
|
+
if (!config.hooks)
|
|
2117
|
+
config.hooks = {};
|
|
2118
|
+
if (!Array.isArray(config.hooks.SessionStart)) {
|
|
2119
|
+
config.hooks.SessionStart = [];
|
|
2120
|
+
}
|
|
2121
|
+
if (hasHook(config)) {
|
|
2122
|
+
return { changed: false, path: file };
|
|
2123
|
+
}
|
|
2124
|
+
config.hooks.SessionStart.push(HOOK_GROUP);
|
|
2125
|
+
await writeJson(file, config);
|
|
2126
|
+
return { changed: true, path: file };
|
|
2127
|
+
}
|
|
2128
|
+
async function removeHook(file) {
|
|
2129
|
+
if (!existsSync6(file))
|
|
2130
|
+
return { changed: false, path: file };
|
|
2131
|
+
const original = await readJson(file);
|
|
2132
|
+
const config = JSON.parse(JSON.stringify(original));
|
|
2133
|
+
if (!config.hooks || !Array.isArray(config.hooks.SessionStart)) {
|
|
2134
|
+
return { changed: false, path: file };
|
|
2135
|
+
}
|
|
2136
|
+
const beforeLen = config.hooks.SessionStart.length;
|
|
2137
|
+
config.hooks.SessionStart = config.hooks.SessionStart.map((group) => {
|
|
2138
|
+
if (!group || !Array.isArray(group.hooks))
|
|
2139
|
+
return group;
|
|
2140
|
+
group.hooks = group.hooks.filter((h) => h?.command !== HOOK_COMMAND);
|
|
2141
|
+
return group;
|
|
2142
|
+
}).filter((group) => Array.isArray(group?.hooks) && group.hooks.length > 0);
|
|
2143
|
+
if (config.hooks.SessionStart.length === 0) {
|
|
2144
|
+
delete config.hooks.SessionStart;
|
|
2145
|
+
}
|
|
2146
|
+
if (config.hooks && Object.keys(config.hooks).length === 0) {
|
|
2147
|
+
delete config.hooks;
|
|
2148
|
+
}
|
|
2149
|
+
const changed = JSON.stringify(config) !== JSON.stringify(original);
|
|
2150
|
+
if (changed) {
|
|
2151
|
+
const isEmpty = !config || Object.keys(config).length === 0;
|
|
2152
|
+
if (isEmpty && existsSync6(file)) {
|
|
2153
|
+
try {
|
|
2154
|
+
unlinkSync(file);
|
|
2155
|
+
} catch {}
|
|
2156
|
+
try {
|
|
2157
|
+
const dir = dirname(file);
|
|
2158
|
+
if (existsSync6(dir) && readdirSync2(dir).length === 0)
|
|
2159
|
+
rmdirSync(dir);
|
|
2160
|
+
} catch {}
|
|
2161
|
+
} else {
|
|
2162
|
+
await writeJson(file, config);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return { changed, path: file };
|
|
2166
|
+
}
|
|
2167
|
+
async function printHookStatus() {
|
|
2168
|
+
const localPath = getLocalHooksPath();
|
|
2169
|
+
const globalPath = getGlobalSettingsPath();
|
|
2170
|
+
const localHas = hasHook(await readJson(localPath));
|
|
2171
|
+
const globalHas = hasHook(await readJson(globalPath));
|
|
2172
|
+
if (localHas) {
|
|
2173
|
+
ui.success(`Enabled in project: ${localPath}`);
|
|
2174
|
+
}
|
|
2175
|
+
if (globalHas) {
|
|
2176
|
+
ui.success(`Enabled globally: ${globalPath}`);
|
|
2177
|
+
}
|
|
2178
|
+
if (!localHas && !globalHas) {
|
|
2179
|
+
ui.info("Journal hook is not installed.");
|
|
2180
|
+
ui.info("Run `dora journal hook enable` to install it.");
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
var HOOK_COMMAND = "sh -c 'dora journal context 2>/dev/null || true'", HOOK_GROUP, enable, disable, status, hook_default;
|
|
2184
|
+
var init_hook = __esm(() => {
|
|
2185
|
+
init_dist();
|
|
2186
|
+
init_out();
|
|
2187
|
+
HOOK_GROUP = {
|
|
2188
|
+
hooks: [
|
|
2189
|
+
{
|
|
2190
|
+
type: "command",
|
|
2191
|
+
command: HOOK_COMMAND
|
|
2192
|
+
}
|
|
2193
|
+
]
|
|
2194
|
+
};
|
|
2195
|
+
enable = defineCommand({
|
|
2196
|
+
meta: {
|
|
2197
|
+
name: "enable",
|
|
2198
|
+
description: "Install the journal decisions hook (SessionStart) so decisions are injected into Claude sessions"
|
|
2199
|
+
},
|
|
2200
|
+
args: {
|
|
2201
|
+
global: {
|
|
2202
|
+
type: "boolean",
|
|
2203
|
+
alias: "g",
|
|
2204
|
+
description: "Install to global ~/.claude/settings.json (instead of project hooks/hooks.json)",
|
|
2205
|
+
default: false
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2208
|
+
async run({ args }) {
|
|
2209
|
+
const useGlobal = !!args.global;
|
|
2210
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
2211
|
+
const result = await addHook(target);
|
|
2212
|
+
if (result.changed) {
|
|
2213
|
+
ui.success(`Enabled journal hook in ${result.path}`);
|
|
2214
|
+
if (useGlobal) {
|
|
2215
|
+
ui.info("Installed globally \u2014 will affect all new Claude sessions (your typical setup with many plugins).");
|
|
2216
|
+
} else {
|
|
2217
|
+
ui.info("Installed locally for this project \u2014 will affect Claude sessions started from this directory.");
|
|
2218
|
+
ui.info("If your Claude hooks live in the global ~/.claude/settings.json (very common), re-run with -g/--global.");
|
|
2219
|
+
}
|
|
2220
|
+
ui.info("Start a new Claude session (or restart) for the hook to take effect.");
|
|
2221
|
+
ui.dim("The hook runs `dora journal context` on SessionStart and injects your active decisions.");
|
|
2222
|
+
ui.info("Preview what gets injected: dora journal context");
|
|
2223
|
+
ui.info("Test inside Claude: ask it to list or recall your recent journal decisions.");
|
|
2224
|
+
} else {
|
|
2225
|
+
ui.info(`Journal hook is already enabled in ${result.path}`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
disable = defineCommand({
|
|
2230
|
+
meta: {
|
|
2231
|
+
name: "disable",
|
|
2232
|
+
description: "Remove the journal decisions hook from Claude configuration"
|
|
2233
|
+
},
|
|
2234
|
+
args: {
|
|
2235
|
+
global: {
|
|
2236
|
+
type: "boolean",
|
|
2237
|
+
alias: "g",
|
|
2238
|
+
description: "Remove from global ~/.claude/settings.json",
|
|
2239
|
+
default: false
|
|
2240
|
+
}
|
|
2241
|
+
},
|
|
2242
|
+
async run({ args }) {
|
|
2243
|
+
const useGlobal = !!args.global;
|
|
2244
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
2245
|
+
const result = await removeHook(target);
|
|
2246
|
+
if (result.changed) {
|
|
2247
|
+
ui.success(`Disabled journal hook in ${result.path}`);
|
|
2248
|
+
ui.info("The decisions will no longer be injected on new SessionStart.");
|
|
2249
|
+
} else {
|
|
2250
|
+
ui.info(`Journal hook was not present in ${result.path}`);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
status = defineCommand({
|
|
2255
|
+
meta: {
|
|
2256
|
+
name: "status",
|
|
2257
|
+
description: "Check where the journal hook is currently installed"
|
|
2258
|
+
},
|
|
2259
|
+
async run() {
|
|
2260
|
+
await printHookStatus();
|
|
2261
|
+
}
|
|
2262
|
+
});
|
|
2263
|
+
hook_default = defineCommand({
|
|
2264
|
+
meta: {
|
|
2265
|
+
name: "hook",
|
|
2266
|
+
description: "Manage Claude hooks for automatically injecting journal decisions"
|
|
2267
|
+
},
|
|
2268
|
+
subCommands: {
|
|
2269
|
+
enable: () => Promise.resolve(enable),
|
|
2270
|
+
disable: () => Promise.resolve(disable),
|
|
2271
|
+
status: () => Promise.resolve(status)
|
|
2272
|
+
},
|
|
2273
|
+
async run() {
|
|
2274
|
+
const cliArgs = process.argv.slice(2);
|
|
2275
|
+
const hookIdx = cliArgs.indexOf("hook");
|
|
2276
|
+
if (hookIdx !== -1 && cliArgs.length > hookIdx + 1)
|
|
2277
|
+
return;
|
|
2278
|
+
await printHookStatus();
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
});
|
|
2282
|
+
|
|
1820
2283
|
// src/cli/commands/journal/update.ts
|
|
1821
2284
|
var exports_update = {};
|
|
1822
2285
|
__export(exports_update, {
|
|
1823
2286
|
default: () => update_default
|
|
1824
2287
|
});
|
|
1825
|
-
import { existsSync as
|
|
1826
|
-
import { join as
|
|
1827
|
-
var
|
|
2288
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2289
|
+
import { join as join6 } from "path";
|
|
2290
|
+
var import_picocolors8, update_default;
|
|
1828
2291
|
var init_update = __esm(() => {
|
|
1829
2292
|
init_dist();
|
|
1830
2293
|
init_out();
|
|
1831
2294
|
init_journal_config();
|
|
1832
2295
|
init_journal_remote();
|
|
1833
|
-
|
|
2296
|
+
import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
1834
2297
|
update_default = defineCommand({
|
|
1835
2298
|
meta: {
|
|
1836
2299
|
name: "update",
|
|
@@ -1851,30 +2314,30 @@ var init_update = __esm(() => {
|
|
|
1851
2314
|
async run({ args }) {
|
|
1852
2315
|
const ghCheck = ensureGhCli();
|
|
1853
2316
|
if (!ghCheck.ok) {
|
|
1854
|
-
ui.write(` ${
|
|
2317
|
+
ui.write(` ${import_picocolors8.default.red("\u2717")} ${import_picocolors8.default.white("The GitHub CLI (")}${import_picocolors8.default.bold("gh")}${import_picocolors8.default.white(") is not installed.")}
|
|
1855
2318
|
`);
|
|
1856
|
-
ui.write(` doraval uses ${
|
|
2319
|
+
ui.write(` doraval uses ${import_picocolors8.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
1857
2320
|
`);
|
|
1858
2321
|
ui.write(` Install it:
|
|
1859
2322
|
`);
|
|
1860
|
-
ui.write(` macOS: ${
|
|
1861
|
-
ui.write(` Linux: ${
|
|
1862
|
-
ui.write(` Windows: ${
|
|
2323
|
+
ui.write(` macOS: ${import_picocolors8.default.dim("brew install gh")}`);
|
|
2324
|
+
ui.write(` Linux: ${import_picocolors8.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
2325
|
+
ui.write(` Windows: ${import_picocolors8.default.dim("winget install --id GitHub.cli")}
|
|
1863
2326
|
`);
|
|
1864
|
-
ui.write(` Then authenticate: ${
|
|
2327
|
+
ui.write(` Then authenticate: ${import_picocolors8.default.dim("gh auth login")}
|
|
1865
2328
|
`);
|
|
1866
2329
|
process.exit(1);
|
|
1867
2330
|
}
|
|
1868
2331
|
const config = await readConfig();
|
|
1869
2332
|
if (!config?.journal.repo) {
|
|
1870
|
-
ui.write(`${
|
|
2333
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} No journal repo configured. Run ${import_picocolors8.default.dim("dora init")} (or ${import_picocolors8.default.dim("doraval journal init")}) first.`);
|
|
1871
2334
|
process.exit(1);
|
|
1872
2335
|
}
|
|
1873
2336
|
const journalRepo = config.journal.repo;
|
|
1874
2337
|
ensureDoravalDirs();
|
|
1875
2338
|
const journalsDir = getJournalsDir();
|
|
1876
2339
|
ui.write(`
|
|
1877
|
-
${
|
|
2340
|
+
${import_picocolors8.default.bold(import_picocolors8.default.white("dora journal update"))} \u2014 ${import_picocolors8.default.dim(import_picocolors8.default.gray(journalRepo))}
|
|
1878
2341
|
`);
|
|
1879
2342
|
const projectsToUpdate = [];
|
|
1880
2343
|
if (args.all) {
|
|
@@ -1892,19 +2355,19 @@ var init_update = __esm(() => {
|
|
|
1892
2355
|
try {
|
|
1893
2356
|
projectsToUpdate.push(sanitizeProjectName(project));
|
|
1894
2357
|
} catch {
|
|
1895
|
-
ui.write(`${
|
|
2358
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Invalid project name: ${project}`);
|
|
1896
2359
|
process.exit(1);
|
|
1897
2360
|
}
|
|
1898
2361
|
}
|
|
1899
2362
|
}
|
|
1900
|
-
const globalLocal =
|
|
2363
|
+
const globalLocal = join6(journalsDir, "global.md");
|
|
1901
2364
|
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", globalLocal);
|
|
1902
2365
|
let gotGlobal;
|
|
1903
2366
|
if (!refreshGlobalRes.ok) {
|
|
1904
2367
|
if (refreshGlobalRes.isNotFound) {
|
|
1905
2368
|
gotGlobal = false;
|
|
1906
2369
|
} else {
|
|
1907
|
-
ui.write(`${
|
|
2370
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch global.md from ${journalRepo}:`);
|
|
1908
2371
|
ui.write(refreshGlobalRes.error);
|
|
1909
2372
|
process.exit(1);
|
|
1910
2373
|
}
|
|
@@ -1912,33 +2375,33 @@ var init_update = __esm(() => {
|
|
|
1912
2375
|
gotGlobal = refreshGlobalRes.value;
|
|
1913
2376
|
}
|
|
1914
2377
|
if (gotGlobal) {
|
|
1915
|
-
ui.write(` ${
|
|
2378
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
|
|
1916
2379
|
} else {
|
|
1917
|
-
ui.write(` ${
|
|
2380
|
+
ui.write(` ${import_picocolors8.default.dim("\xB7")} global.md ${import_picocolors8.default.dim("(not present on remote)")}`);
|
|
1918
2381
|
}
|
|
1919
2382
|
if (projectsToUpdate.length === 0) {
|
|
1920
2383
|
if (args.all) {
|
|
1921
2384
|
ui.write(`
|
|
1922
|
-
${
|
|
2385
|
+
${import_picocolors8.default.dim(import_picocolors8.default.gray("No projects registered."))}
|
|
1923
2386
|
`);
|
|
1924
2387
|
} else {
|
|
1925
2388
|
ui.write(`
|
|
1926
|
-
${
|
|
1927
|
-
` + ` Run ${
|
|
2389
|
+
${import_picocolors8.default.yellow("\u26A0")} No project mapping found.
|
|
2390
|
+
` + ` Run ${import_picocolors8.default.dim("dora init")} or pass ${import_picocolors8.default.dim("--project <name>")} / ${import_picocolors8.default.dim("--all")}.
|
|
1928
2391
|
`);
|
|
1929
2392
|
}
|
|
1930
2393
|
return;
|
|
1931
2394
|
}
|
|
1932
2395
|
for (const project of projectsToUpdate) {
|
|
1933
2396
|
const remotePath = `projects/${project}.md`;
|
|
1934
|
-
const localPath =
|
|
2397
|
+
const localPath = join6(journalsDir, `${project}.md`);
|
|
1935
2398
|
const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localPath);
|
|
1936
2399
|
let got;
|
|
1937
2400
|
if (!refreshRes.ok) {
|
|
1938
2401
|
if (refreshRes.isNotFound) {
|
|
1939
2402
|
got = false;
|
|
1940
2403
|
} else {
|
|
1941
|
-
ui.write(`${
|
|
2404
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch ${remotePath} from ${journalRepo}:`);
|
|
1942
2405
|
ui.write(refreshRes.error);
|
|
1943
2406
|
process.exit(1);
|
|
1944
2407
|
}
|
|
@@ -1946,10 +2409,10 @@ var init_update = __esm(() => {
|
|
|
1946
2409
|
got = refreshRes.value;
|
|
1947
2410
|
}
|
|
1948
2411
|
if (got) {
|
|
1949
|
-
ui.write(` ${
|
|
2412
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
|
|
1950
2413
|
} else {
|
|
1951
|
-
ui.write(` ${
|
|
1952
|
-
if (!
|
|
2414
|
+
ui.write(` ${import_picocolors8.default.dim("\xB7")} ${remotePath} ${import_picocolors8.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
|
|
2415
|
+
if (!existsSync7(localPath)) {
|
|
1953
2416
|
await Bun.write(localPath, `# ${project} Journal
|
|
1954
2417
|
|
|
1955
2418
|
Project-specific decisions.
|
|
@@ -1959,7 +2422,7 @@ Project-specific decisions.
|
|
|
1959
2422
|
}
|
|
1960
2423
|
const summary = args.all && projectsToUpdate.length > 1 ? `${projectsToUpdate.length} projects + global` : projectsToUpdate.length === 1 ? projectsToUpdate[0] : "journals";
|
|
1961
2424
|
ui.write(`
|
|
1962
|
-
${
|
|
2425
|
+
${import_picocolors8.default.dim(import_picocolors8.default.gray("Local cache refreshed for"))} ${import_picocolors8.default.bold(import_picocolors8.default.white(summary))}.
|
|
1963
2426
|
`);
|
|
1964
2427
|
}
|
|
1965
2428
|
});
|
|
@@ -2026,8 +2489,8 @@ __export(exports_add, {
|
|
|
2026
2489
|
default: () => add_default,
|
|
2027
2490
|
buildAgentArgv: () => buildAgentArgv
|
|
2028
2491
|
});
|
|
2029
|
-
import { existsSync as
|
|
2030
|
-
import { join as
|
|
2492
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2493
|
+
import { join as join7 } from "path";
|
|
2031
2494
|
var {spawnSync: spawnSync2 } = globalThis.Bun;
|
|
2032
2495
|
function buildAgentArgv(template, promptText) {
|
|
2033
2496
|
const marker = "__DORA_PROMPT__";
|
|
@@ -2067,7 +2530,7 @@ If you cannot produce exactly this, output the JSON with the best you can and se
|
|
|
2067
2530
|
const template = agentCfg.prompt_template || '-p "{{prompt}}" --output-format json';
|
|
2068
2531
|
const extraArgs = buildAgentArgv(template, scaffold);
|
|
2069
2532
|
const shortTemplate = (agentCfg.prompt_template || '-p "{{prompt}}" --output-format json').slice(0, 80);
|
|
2070
|
-
ui.write(` ${
|
|
2533
|
+
ui.write(` ${import_picocolors9.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
|
|
2071
2534
|
try {
|
|
2072
2535
|
const result = spawnSync2([agentCfg.command, ...extraArgs], {
|
|
2073
2536
|
stdout: "pipe",
|
|
@@ -2076,12 +2539,12 @@ If you cannot produce exactly this, output the JSON with the best you can and se
|
|
|
2076
2539
|
const stdout = result.stdout.toString().trim();
|
|
2077
2540
|
const stderr = result.stderr.toString().trim();
|
|
2078
2541
|
if (result.exitCode !== 0) {
|
|
2079
|
-
ui.write(` ${
|
|
2542
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
|
|
2080
2543
|
if (stderr)
|
|
2081
|
-
ui.write(` ${
|
|
2544
|
+
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2082
2545
|
${stderr.slice(0, 800)}`);
|
|
2083
2546
|
if (stdout)
|
|
2084
|
-
ui.write(` ${
|
|
2547
|
+
ui.write(` ${import_picocolors9.default.dim("stdout:")}
|
|
2085
2548
|
${stdout.slice(0, 400)}`);
|
|
2086
2549
|
return null;
|
|
2087
2550
|
}
|
|
@@ -2126,37 +2589,37 @@ ${stdout.slice(0, 400)}`);
|
|
|
2126
2589
|
parsed = candidates[0] || null;
|
|
2127
2590
|
}
|
|
2128
2591
|
if (!parsed || typeof parsed !== "object") {
|
|
2129
|
-
ui.write(` ${
|
|
2130
|
-
ui.write(` ${
|
|
2592
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
|
|
2593
|
+
ui.write(` ${import_picocolors9.default.dim("stdout (first 700 chars):")}
|
|
2131
2594
|
${stdout.slice(0, 700)}`);
|
|
2132
2595
|
if (stderr)
|
|
2133
|
-
ui.write(` ${
|
|
2596
|
+
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2134
2597
|
${stderr.slice(0, 500)}`);
|
|
2135
2598
|
return null;
|
|
2136
2599
|
}
|
|
2137
2600
|
if (!parsed.title && !parsed.rationale) {
|
|
2138
|
-
ui.write(` ${
|
|
2139
|
-
ui.write(` ${
|
|
2140
|
-
ui.write(` ${
|
|
2601
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
|
|
2602
|
+
ui.write(` ${import_picocolors9.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
|
|
2603
|
+
ui.write(` ${import_picocolors9.default.dim("stdout (truncated):")}
|
|
2141
2604
|
${stdout.slice(0, 600)}`);
|
|
2142
2605
|
return null;
|
|
2143
2606
|
}
|
|
2144
2607
|
return parsed;
|
|
2145
2608
|
} catch (e) {
|
|
2146
|
-
ui.write(` ${
|
|
2609
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
|
|
2147
2610
|
return null;
|
|
2148
2611
|
}
|
|
2149
2612
|
}
|
|
2150
2613
|
function slugify(title) {
|
|
2151
2614
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
|
|
2152
2615
|
}
|
|
2153
|
-
var
|
|
2616
|
+
var import_picocolors9, add_default;
|
|
2154
2617
|
var init_add = __esm(() => {
|
|
2155
2618
|
init_dist();
|
|
2156
2619
|
init_out();
|
|
2157
2620
|
init_journal_config();
|
|
2158
2621
|
init_journal_validate();
|
|
2159
|
-
|
|
2622
|
+
import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
2160
2623
|
add_default = defineCommand({
|
|
2161
2624
|
meta: {
|
|
2162
2625
|
name: "add",
|
|
@@ -2231,16 +2694,16 @@ var init_add = __esm(() => {
|
|
|
2231
2694
|
project = sanitizeProjectName(project);
|
|
2232
2695
|
}
|
|
2233
2696
|
if (!project) {
|
|
2234
|
-
ui.write(`${
|
|
2697
|
+
ui.write(`${import_picocolors9.default.yellow("\u26A0")} No project mapping found.
|
|
2235
2698
|
|
|
2236
|
-
` + `Run ${
|
|
2699
|
+
` + `Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first, or pass ${import_picocolors9.default.dim("--project <name>")}.`);
|
|
2237
2700
|
process.exit(1);
|
|
2238
2701
|
}
|
|
2239
2702
|
let title;
|
|
2240
2703
|
let pushback;
|
|
2241
2704
|
let tags = [];
|
|
2242
2705
|
let author = String(args.author || "human");
|
|
2243
|
-
let
|
|
2706
|
+
let status2 = args.status || "active";
|
|
2244
2707
|
let rationale;
|
|
2245
2708
|
let date = new Date().toISOString().split("T")[0];
|
|
2246
2709
|
const jsonInput = args.json;
|
|
@@ -2267,11 +2730,11 @@ var init_add = __esm(() => {
|
|
|
2267
2730
|
if (parsed.author)
|
|
2268
2731
|
author = String(parsed.author);
|
|
2269
2732
|
if (parsed.status)
|
|
2270
|
-
|
|
2733
|
+
status2 = parsed.status;
|
|
2271
2734
|
if (parsed.date)
|
|
2272
2735
|
date = String(parsed.date);
|
|
2273
2736
|
} catch (e) {
|
|
2274
|
-
ui.write(`${
|
|
2737
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
|
|
2275
2738
|
process.exit(1);
|
|
2276
2739
|
}
|
|
2277
2740
|
}
|
|
@@ -2280,7 +2743,7 @@ var init_add = __esm(() => {
|
|
|
2280
2743
|
if (rawMdArg && !jsonInput) {
|
|
2281
2744
|
if (rawMdArg === "-" || rawMdArg === "") {
|
|
2282
2745
|
rawBody = (await new Response(Bun.stdin.stream()).text()).trim();
|
|
2283
|
-
} else if (
|
|
2746
|
+
} else if (existsSync8(rawMdArg)) {
|
|
2284
2747
|
rawBody = (await Bun.file(rawMdArg).text()).trim();
|
|
2285
2748
|
} else {
|
|
2286
2749
|
rawBody = rawMdArg.trim();
|
|
@@ -2295,7 +2758,7 @@ var init_add = __esm(() => {
|
|
|
2295
2758
|
title = (headingMatch[1] ?? "").trim();
|
|
2296
2759
|
rawBody = rawBody.replace(/^#+\s+(.+?)(?:\r?\n|$)/m, "").trimStart();
|
|
2297
2760
|
} else {
|
|
2298
|
-
ui.write(`${
|
|
2761
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
|
|
2299
2762
|
process.exit(1);
|
|
2300
2763
|
}
|
|
2301
2764
|
}
|
|
@@ -2335,7 +2798,7 @@ var init_add = __esm(() => {
|
|
|
2335
2798
|
agentCfg = fullConfigForAgent?.agent;
|
|
2336
2799
|
if (agentCfg) {
|
|
2337
2800
|
attemptedAgent = true;
|
|
2338
|
-
ui.write(` ${
|
|
2801
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("(querying your configured coding agent...)"))}`);
|
|
2339
2802
|
const agentResult = await invokeConfiguredAgentForEntry(title, agentCfg);
|
|
2340
2803
|
if (agentResult) {
|
|
2341
2804
|
if (agentResult.title)
|
|
@@ -2352,7 +2815,7 @@ var init_add = __esm(() => {
|
|
|
2352
2815
|
if (agentResult.author)
|
|
2353
2816
|
author = String(agentResult.author);
|
|
2354
2817
|
if (agentResult.status)
|
|
2355
|
-
|
|
2818
|
+
status2 = agentResult.status;
|
|
2356
2819
|
if (agentResult.date)
|
|
2357
2820
|
date = String(agentResult.date);
|
|
2358
2821
|
}
|
|
@@ -2364,22 +2827,22 @@ var init_add = __esm(() => {
|
|
|
2364
2827
|
tags,
|
|
2365
2828
|
author,
|
|
2366
2829
|
date,
|
|
2367
|
-
status
|
|
2830
|
+
status: status2
|
|
2368
2831
|
};
|
|
2369
2832
|
const validation = validateEntry(entry);
|
|
2370
2833
|
if (!validation.valid) {
|
|
2371
|
-
ui.write(`${
|
|
2834
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} Invalid entry:
|
|
2372
2835
|
`);
|
|
2373
2836
|
for (const err of validation.errors) {
|
|
2374
|
-
ui.write(` ${
|
|
2837
|
+
ui.write(` ${import_picocolors9.default.red("\u2022")} ${err}`);
|
|
2375
2838
|
}
|
|
2376
2839
|
process.exit(1);
|
|
2377
2840
|
}
|
|
2378
2841
|
for (const warn of validation.warnings) {
|
|
2379
2842
|
if ((warn.includes("not supplied") || warn.includes("empty")) && attemptedAgent) {} else if (warn.includes("not supplied") || warn.includes("empty")) {
|
|
2380
|
-
ui.write(`${
|
|
2843
|
+
ui.write(`${import_picocolors9.default.dim("\xB7")} ${warn}`);
|
|
2381
2844
|
} else {
|
|
2382
|
-
ui.write(`${
|
|
2845
|
+
ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${warn}`);
|
|
2383
2846
|
}
|
|
2384
2847
|
}
|
|
2385
2848
|
if (!rationale) {
|
|
@@ -2392,38 +2855,38 @@ pushback: ${pushback}
|
|
|
2392
2855
|
tags: [${tags.join(", ")}]
|
|
2393
2856
|
author: ${author}
|
|
2394
2857
|
date: ${date}
|
|
2395
|
-
status: ${
|
|
2858
|
+
status: ${status2}
|
|
2396
2859
|
\`\`\`
|
|
2397
2860
|
|
|
2398
2861
|
${rationale}
|
|
2399
2862
|
`;
|
|
2400
2863
|
ensureDoravalDirs();
|
|
2401
2864
|
const pendingDir = getPendingProjectDir(project);
|
|
2402
|
-
if (!
|
|
2403
|
-
await Bun.write(
|
|
2865
|
+
if (!existsSync8(pendingDir)) {
|
|
2866
|
+
await Bun.write(join7(pendingDir, ".gitkeep"), "");
|
|
2404
2867
|
}
|
|
2405
2868
|
const slug = slugify(title);
|
|
2406
2869
|
const filename = `${date}-${slug}.md`;
|
|
2407
|
-
const filePath =
|
|
2870
|
+
const filePath = join7(pendingDir, filename);
|
|
2408
2871
|
await Bun.write(filePath, content);
|
|
2409
2872
|
ui.write(`
|
|
2410
|
-
${
|
|
2411
|
-
ui.write(` Project: ${
|
|
2873
|
+
${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.bold(import_picocolors9.default.white(title))}`);
|
|
2874
|
+
ui.write(` Project: ${import_picocolors9.default.white(project)} \xB7 run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))} to publish
|
|
2412
2875
|
`);
|
|
2413
2876
|
if (args.verbose) {
|
|
2414
|
-
const authorDisplay = author.startsWith("agent:") ?
|
|
2415
|
-
ui.write(` Pushback: ${
|
|
2416
|
-
ui.write(` Tags: ${
|
|
2877
|
+
const authorDisplay = author.startsWith("agent:") ? import_picocolors9.default.cyan(author) : author;
|
|
2878
|
+
ui.write(` Pushback: ${import_picocolors9.default.white(String(pushback))}`);
|
|
2879
|
+
ui.write(` Tags: ${import_picocolors9.default.gray(tags.join(", ") || import_picocolors9.default.dim("(none)"))}`);
|
|
2417
2880
|
ui.write(` Author: ${authorDisplay}`);
|
|
2418
|
-
ui.write(` File: ${
|
|
2881
|
+
ui.write(` File: ${import_picocolors9.default.dim(import_picocolors9.default.gray(filePath))}
|
|
2419
2882
|
`);
|
|
2420
2883
|
}
|
|
2421
2884
|
if (isThinInput && !author.startsWith("agent:")) {
|
|
2422
2885
|
if (attemptedAgent) {
|
|
2423
|
-
ui.write(` ${
|
|
2886
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
|
|
2424
2887
|
`);
|
|
2425
2888
|
} else {
|
|
2426
|
-
ui.write(` ${
|
|
2889
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
|
|
2427
2890
|
`);
|
|
2428
2891
|
}
|
|
2429
2892
|
}
|
|
@@ -2437,8 +2900,8 @@ var exports_sync = {};
|
|
|
2437
2900
|
__export(exports_sync, {
|
|
2438
2901
|
default: () => sync_default
|
|
2439
2902
|
});
|
|
2440
|
-
import { readdirSync as
|
|
2441
|
-
import { join as
|
|
2903
|
+
import { readdirSync as readdirSync3, existsSync as existsSync9 } from "fs";
|
|
2904
|
+
import { join as join8 } from "path";
|
|
2442
2905
|
var {spawnSync: spawnSync3 } = globalThis.Bun;
|
|
2443
2906
|
function updateGitHubFile(repo, path, content, message, sha) {
|
|
2444
2907
|
const payload = {
|
|
@@ -2467,18 +2930,18 @@ function updateGitHubFile(repo, path, content, message, sha) {
|
|
|
2467
2930
|
stderr: "pipe"
|
|
2468
2931
|
});
|
|
2469
2932
|
if (result.exitCode !== 0) {
|
|
2470
|
-
ui.write(
|
|
2933
|
+
ui.write(import_picocolors10.default.red(`Failed to update ${path} on ${repo}:`));
|
|
2471
2934
|
ui.write(result.stderr.toString());
|
|
2472
2935
|
process.exit(1);
|
|
2473
2936
|
}
|
|
2474
2937
|
}
|
|
2475
|
-
var
|
|
2938
|
+
var import_picocolors10, sync_default;
|
|
2476
2939
|
var init_sync = __esm(() => {
|
|
2477
2940
|
init_dist();
|
|
2478
2941
|
init_out();
|
|
2479
2942
|
init_journal_config();
|
|
2480
2943
|
init_journal_remote();
|
|
2481
|
-
|
|
2944
|
+
import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
2482
2945
|
sync_default = defineCommand({
|
|
2483
2946
|
meta: {
|
|
2484
2947
|
name: "sync",
|
|
@@ -2512,70 +2975,70 @@ var init_sync = __esm(() => {
|
|
|
2512
2975
|
project = sanitizeProjectName(project);
|
|
2513
2976
|
}
|
|
2514
2977
|
if (!project) {
|
|
2515
|
-
ui.write(`${
|
|
2978
|
+
ui.write(`${import_picocolors10.default.yellow("\u26A0")} No project mapping found.
|
|
2516
2979
|
|
|
2517
|
-
` + `Run ${
|
|
2980
|
+
` + `Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first, or pass ${import_picocolors10.default.dim("--project <name>")}.`);
|
|
2518
2981
|
process.exit(1);
|
|
2519
2982
|
}
|
|
2520
2983
|
if (!config?.journal.repo) {
|
|
2521
|
-
ui.write(`${
|
|
2984
|
+
ui.write(`${import_picocolors10.default.red("\u2717")} No journal repo configured. Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first.`);
|
|
2522
2985
|
process.exit(1);
|
|
2523
2986
|
}
|
|
2524
2987
|
const ghCheck = ensureGhCli();
|
|
2525
2988
|
if (!ghCheck.ok) {
|
|
2526
|
-
ui.write(` ${
|
|
2989
|
+
ui.write(` ${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("The GitHub CLI (")}${import_picocolors10.default.bold("gh")}${import_picocolors10.default.white(") is not installed.")}
|
|
2527
2990
|
`);
|
|
2528
|
-
ui.write(` doraval uses ${
|
|
2991
|
+
ui.write(` doraval uses ${import_picocolors10.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
2529
2992
|
`);
|
|
2530
2993
|
ui.write(` Install it:
|
|
2531
2994
|
`);
|
|
2532
|
-
ui.write(` macOS: ${
|
|
2533
|
-
ui.write(` Linux: ${
|
|
2534
|
-
ui.write(` Windows: ${
|
|
2995
|
+
ui.write(` macOS: ${import_picocolors10.default.dim("brew install gh")}`);
|
|
2996
|
+
ui.write(` Linux: ${import_picocolors10.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
2997
|
+
ui.write(` Windows: ${import_picocolors10.default.dim("winget install --id GitHub.cli")}
|
|
2535
2998
|
`);
|
|
2536
|
-
ui.write(` Then authenticate: ${
|
|
2999
|
+
ui.write(` Then authenticate: ${import_picocolors10.default.dim("gh auth login")}
|
|
2537
3000
|
`);
|
|
2538
3001
|
process.exit(1);
|
|
2539
3002
|
}
|
|
2540
3003
|
const journalRepo = config.journal.repo;
|
|
2541
3004
|
const pendingDir = getPendingProjectDir(project);
|
|
2542
3005
|
ui.write(`
|
|
2543
|
-
${
|
|
3006
|
+
${import_picocolors10.default.bold(import_picocolors10.default.white("dora journal sync"))} \u2014 ${import_picocolors10.default.white(project)}
|
|
2544
3007
|
`);
|
|
2545
|
-
ui.write(` Journal repo: ${
|
|
3008
|
+
ui.write(` Journal repo: ${import_picocolors10.default.dim(import_picocolors10.default.gray(journalRepo))}`);
|
|
2546
3009
|
ensureDoravalDirs();
|
|
2547
3010
|
const journalsDir = getJournalsDir();
|
|
2548
3011
|
const remoteProjectPath = `projects/${project}.md`;
|
|
2549
|
-
const localProjectPath =
|
|
2550
|
-
ui.write(` ${
|
|
2551
|
-
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md",
|
|
3012
|
+
const localProjectPath = join8(journalsDir, `${project}.md`);
|
|
3013
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Refreshing local cache from remote..."))}`);
|
|
3014
|
+
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", join8(journalsDir, "global.md"));
|
|
2552
3015
|
if (!refreshGlobalRes.ok) {
|
|
2553
3016
|
if (!refreshGlobalRes.isNotFound) {
|
|
2554
|
-
ui.write(
|
|
3017
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch global.md from ${journalRepo}:`));
|
|
2555
3018
|
ui.write(refreshGlobalRes.error);
|
|
2556
3019
|
process.exit(1);
|
|
2557
3020
|
}
|
|
2558
3021
|
}
|
|
2559
3022
|
const gotGlobal = refreshGlobalRes.ok && refreshGlobalRes.value;
|
|
2560
3023
|
if (gotGlobal) {
|
|
2561
|
-
ui.write(` ${
|
|
3024
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("\u2713 global.md"))}`);
|
|
2562
3025
|
}
|
|
2563
3026
|
const refreshProjectCacheRes = await refreshLocalJournalFile(journalRepo, remoteProjectPath, localProjectPath);
|
|
2564
3027
|
if (!refreshProjectCacheRes.ok) {
|
|
2565
3028
|
if (!refreshProjectCacheRes.isNotFound) {
|
|
2566
|
-
ui.write(
|
|
3029
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch ${remoteProjectPath} from ${journalRepo}:`));
|
|
2567
3030
|
ui.write(refreshProjectCacheRes.error);
|
|
2568
3031
|
process.exit(1);
|
|
2569
3032
|
}
|
|
2570
3033
|
}
|
|
2571
3034
|
const gotProjectCache = refreshProjectCacheRes.ok && refreshProjectCacheRes.value;
|
|
2572
3035
|
if (gotProjectCache) {
|
|
2573
|
-
ui.write(` ${
|
|
3036
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray(`\u2713 ${remoteProjectPath}`))}`);
|
|
2574
3037
|
}
|
|
2575
|
-
const pendingFiles =
|
|
3038
|
+
const pendingFiles = existsSync9(pendingDir) ? readdirSync3(pendingDir).filter((f) => f.endsWith(".md") && f !== ".gitkeep").sort() : [];
|
|
2576
3039
|
if (pendingFiles.length === 0) {
|
|
2577
3040
|
ui.write(`
|
|
2578
|
-
${
|
|
3041
|
+
${import_picocolors10.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
|
|
2579
3042
|
`);
|
|
2580
3043
|
process.exit(0);
|
|
2581
3044
|
}
|
|
@@ -2588,7 +3051,7 @@ var init_sync = __esm(() => {
|
|
|
2588
3051
|
let currentFile = null;
|
|
2589
3052
|
if (!metaRes.ok) {
|
|
2590
3053
|
if (!metaRes.isNotFound) {
|
|
2591
|
-
ui.write(
|
|
3054
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch ${remotePath} from ${journalRepo}:`));
|
|
2592
3055
|
ui.write(metaRes.error);
|
|
2593
3056
|
process.exit(1);
|
|
2594
3057
|
}
|
|
@@ -2599,14 +3062,14 @@ var init_sync = __esm(() => {
|
|
|
2599
3062
|
existingContent = Buffer.from(currentFile.content, "base64").toString("utf8");
|
|
2600
3063
|
currentSha = currentFile.sha;
|
|
2601
3064
|
if (args.verbose)
|
|
2602
|
-
ui.write(` ${
|
|
3065
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Found existing remote file (sha: " + (currentSha?.slice(0, 7) ?? "") + "...)"))}`);
|
|
2603
3066
|
} else {
|
|
2604
3067
|
if (args.verbose)
|
|
2605
|
-
ui.write(` ${
|
|
3068
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("No existing file on remote \u2014 will create it"))}`);
|
|
2606
3069
|
}
|
|
2607
3070
|
let newEntries = "";
|
|
2608
3071
|
for (const file of pendingFiles) {
|
|
2609
|
-
const fullPath =
|
|
3072
|
+
const fullPath = join8(pendingDir, file);
|
|
2610
3073
|
const entryContent = await Bun.file(fullPath).text();
|
|
2611
3074
|
newEntries += `
|
|
2612
3075
|
` + entryContent.trim() + `
|
|
@@ -2626,36 +3089,36 @@ var init_sync = __esm(() => {
|
|
|
2626
3089
|
const commitMessage = args.message || `journal: add ${pendingFiles.length} entr${pendingFiles.length === 1 ? "y" : "ies"} for ${project}`;
|
|
2627
3090
|
if (args.verbose)
|
|
2628
3091
|
ui.write(`
|
|
2629
|
-
${
|
|
3092
|
+
${import_picocolors10.default.dim(import_picocolors10.default.gray("Pushing to remote..."))}`);
|
|
2630
3093
|
try {
|
|
2631
3094
|
updateGitHubFile(journalRepo, remotePath, newContent, commitMessage, currentSha);
|
|
2632
|
-
ui.write(` ${
|
|
3095
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Successfully pushed to")} ${import_picocolors10.default.white(remotePath)}`);
|
|
2633
3096
|
} catch (err) {
|
|
2634
|
-
ui.write(`${
|
|
3097
|
+
ui.write(`${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("Failed to push to GitHub.")}`);
|
|
2635
3098
|
process.exit(1);
|
|
2636
3099
|
}
|
|
2637
3100
|
for (const file of pendingFiles) {
|
|
2638
|
-
const fullPath =
|
|
3101
|
+
const fullPath = join8(pendingDir, file);
|
|
2639
3102
|
try {
|
|
2640
3103
|
await Bun.file(fullPath).unlink();
|
|
2641
3104
|
} catch {}
|
|
2642
3105
|
}
|
|
2643
|
-
ui.write(` ${
|
|
3106
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Cleared local pending entries")}`);
|
|
2644
3107
|
try {
|
|
2645
3108
|
const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localProjectPath);
|
|
2646
3109
|
if (!refreshRes.ok) {
|
|
2647
3110
|
if (!refreshRes.isNotFound) {
|
|
2648
|
-
ui.write(` ${
|
|
3111
|
+
ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
|
|
2649
3112
|
}
|
|
2650
3113
|
} else if (refreshRes.value) {
|
|
2651
3114
|
if (args.verbose)
|
|
2652
|
-
ui.write(` ${
|
|
3115
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Re-fetched")} ${import_picocolors10.default.white(project)}.md ${import_picocolors10.default.white("into local cache")}`);
|
|
2653
3116
|
}
|
|
2654
3117
|
} catch {
|
|
2655
|
-
ui.write(` ${
|
|
3118
|
+
ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
|
|
2656
3119
|
}
|
|
2657
3120
|
ui.write(`
|
|
2658
|
-
${
|
|
3121
|
+
${import_picocolors10.default.green("Done!")} ${import_picocolors10.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
|
|
2659
3122
|
`);
|
|
2660
3123
|
process.exit(0);
|
|
2661
3124
|
}
|
|
@@ -2714,15 +3177,15 @@ var init_spec = __esm(() => {
|
|
|
2714
3177
|
});
|
|
2715
3178
|
|
|
2716
3179
|
// src/cli/commands/claude/context.ts
|
|
2717
|
-
import { existsSync as
|
|
2718
|
-
import { join as
|
|
3180
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
|
|
3181
|
+
import { join as join9 } from "path";
|
|
2719
3182
|
function detectContext(cwd = process.cwd()) {
|
|
2720
3183
|
const claudeSpec = getProviderSpec("claude");
|
|
2721
|
-
const hasClaudeDir =
|
|
2722
|
-
const hasPluginManifest =
|
|
3184
|
+
const hasClaudeDir = existsSync10(join9(cwd, ".claude"));
|
|
3185
|
+
const hasPluginManifest = existsSync10(join9(cwd, claudeSpec.manifestPath));
|
|
2723
3186
|
let looseSkillFiles = [];
|
|
2724
3187
|
try {
|
|
2725
|
-
const files =
|
|
3188
|
+
const files = readdirSync4(cwd);
|
|
2726
3189
|
looseSkillFiles = files.filter((f) => {
|
|
2727
3190
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
2728
3191
|
return false;
|
|
@@ -2741,7 +3204,7 @@ function detectContext(cwd = process.cwd()) {
|
|
|
2741
3204
|
isEmpty
|
|
2742
3205
|
};
|
|
2743
3206
|
}
|
|
2744
|
-
var
|
|
3207
|
+
var init_context2 = __esm(() => {
|
|
2745
3208
|
init_spec();
|
|
2746
3209
|
});
|
|
2747
3210
|
|
|
@@ -2752,8 +3215,8 @@ __export(exports_new, {
|
|
|
2752
3215
|
default: () => new_default,
|
|
2753
3216
|
decidePath: () => decidePath
|
|
2754
3217
|
});
|
|
2755
|
-
import { join as
|
|
2756
|
-
import { mkdirSync as
|
|
3218
|
+
import { join as join10, basename as basename2, dirname as dirname2 } from "path";
|
|
3219
|
+
import { mkdirSync as mkdirSync3, writeFileSync, existsSync as existsSync11 } from "fs";
|
|
2757
3220
|
function decidePath(ctx, intent, providedName) {
|
|
2758
3221
|
const rawName = providedName || "";
|
|
2759
3222
|
let decisionPath = "standalone";
|
|
@@ -2767,7 +3230,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2767
3230
|
targetDir = ctx.cwd;
|
|
2768
3231
|
shouldCreateDir = false;
|
|
2769
3232
|
} else {
|
|
2770
|
-
targetDir =
|
|
3233
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2771
3234
|
shouldCreateDir = true;
|
|
2772
3235
|
}
|
|
2773
3236
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -2777,7 +3240,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2777
3240
|
targetDir = ctx.cwd;
|
|
2778
3241
|
shouldCreateDir = false;
|
|
2779
3242
|
} else {
|
|
2780
|
-
targetDir =
|
|
3243
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2781
3244
|
shouldCreateDir = true;
|
|
2782
3245
|
}
|
|
2783
3246
|
} else if (decisionPath === "standalone") {
|
|
@@ -2785,7 +3248,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2785
3248
|
targetDir = ctx.cwd;
|
|
2786
3249
|
shouldCreateDir = false;
|
|
2787
3250
|
} else {
|
|
2788
|
-
targetDir =
|
|
3251
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2789
3252
|
shouldCreateDir = true;
|
|
2790
3253
|
}
|
|
2791
3254
|
}
|
|
@@ -2793,24 +3256,25 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2793
3256
|
}
|
|
2794
3257
|
function scaffold(decision, ctx, migrateContent) {
|
|
2795
3258
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
2796
|
-
if (
|
|
3259
|
+
if (existsSync11(targetDir) && shouldCreateDir) {
|
|
2797
3260
|
ui.fail("Target already exists");
|
|
2798
3261
|
process.exit(1);
|
|
2799
3262
|
}
|
|
2800
3263
|
if (shouldCreateDir) {
|
|
2801
|
-
|
|
3264
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
2802
3265
|
}
|
|
2803
3266
|
if (path === "plugin") {
|
|
2804
3267
|
const pluginName = basename2(targetDir);
|
|
2805
3268
|
const claudeSpec = getProviderSpec("claude");
|
|
2806
|
-
const claudeManifestDir =
|
|
3269
|
+
const claudeManifestDir = dirname2(claudeSpec.manifestPath);
|
|
2807
3270
|
const pluginJson = {
|
|
2808
3271
|
name: pluginName,
|
|
2809
3272
|
description: "Scaffolded by doraval claude new",
|
|
2810
|
-
version: "0.1.0"
|
|
3273
|
+
version: "0.1.0",
|
|
3274
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
2811
3275
|
};
|
|
2812
|
-
|
|
2813
|
-
writeFileSync(
|
|
3276
|
+
mkdirSync3(join10(targetDir, claudeManifestDir), { recursive: true });
|
|
3277
|
+
writeFileSync(join10(targetDir, claudeSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
2814
3278
|
const marketplaceJson = {
|
|
2815
3279
|
name: pluginName,
|
|
2816
3280
|
version: "0.1.0",
|
|
@@ -2821,9 +3285,9 @@ function scaffold(decision, ctx, migrateContent) {
|
|
|
2821
3285
|
license: "MIT",
|
|
2822
3286
|
keywords: ["claude-code", "skills", "plugin"]
|
|
2823
3287
|
};
|
|
2824
|
-
writeFileSync(
|
|
3288
|
+
writeFileSync(join10(targetDir, "marketplace.json"), JSON.stringify(marketplaceJson, null, 2));
|
|
2825
3289
|
const demoSkillName = "doraval";
|
|
2826
|
-
|
|
3290
|
+
mkdirSync3(join10(targetDir, "skills", demoSkillName), { recursive: true });
|
|
2827
3291
|
let skillContent;
|
|
2828
3292
|
if (migrateContent) {
|
|
2829
3293
|
skillContent = migrateContent;
|
|
@@ -2847,19 +3311,19 @@ When you need to check a skill or plugin:
|
|
|
2847
3311
|
|
|
2848
3312
|
Always run \`doraval validate\` before sharing or publishing a plugin. This skill demonstrates a complete, self-referential example of using doraval inside a generated plugin.`;
|
|
2849
3313
|
}
|
|
2850
|
-
writeFileSync(
|
|
2851
|
-
const readmePath =
|
|
2852
|
-
if (!
|
|
3314
|
+
writeFileSync(join10(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
3315
|
+
const readmePath = join10(targetDir, "README.md");
|
|
3316
|
+
if (!existsSync11(readmePath)) {
|
|
2853
3317
|
writeFileSync(readmePath, "# " + pluginName + `
|
|
2854
3318
|
|
|
2855
3319
|
Claude Code plugin scaffolded by doraval.`);
|
|
2856
3320
|
}
|
|
2857
3321
|
} else {
|
|
2858
|
-
|
|
3322
|
+
mkdirSync3(join10(targetDir, ".claude", "skills", "my-skill"), { recursive: true });
|
|
2859
3323
|
const skillBody = migrateContent || `# My Skill
|
|
2860
3324
|
|
|
2861
3325
|
Basic starter.`;
|
|
2862
|
-
writeFileSync(
|
|
3326
|
+
writeFileSync(join10(targetDir, ".claude", "skills", "my-skill", "SKILL.md"), `---
|
|
2863
3327
|
name: my-skill
|
|
2864
3328
|
description: Starter
|
|
2865
3329
|
---
|
|
@@ -2867,14 +3331,14 @@ description: Starter
|
|
|
2867
3331
|
${skillBody}`);
|
|
2868
3332
|
}
|
|
2869
3333
|
}
|
|
2870
|
-
var
|
|
3334
|
+
var import_picocolors11, new_default;
|
|
2871
3335
|
var init_new = __esm(() => {
|
|
2872
3336
|
init_dist();
|
|
2873
3337
|
init_out();
|
|
2874
|
-
|
|
3338
|
+
init_context2();
|
|
2875
3339
|
init_prompt();
|
|
2876
3340
|
init_spec();
|
|
2877
|
-
|
|
3341
|
+
import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
2878
3342
|
new_default = defineCommand({
|
|
2879
3343
|
meta: {
|
|
2880
3344
|
name: "new",
|
|
@@ -2913,7 +3377,7 @@ var init_new = __esm(() => {
|
|
|
2913
3377
|
}
|
|
2914
3378
|
scaffold(decision, ctx, migrateContent);
|
|
2915
3379
|
ui.write(`
|
|
2916
|
-
${
|
|
3380
|
+
${import_picocolors11.default.green("\u2713")} Created ${decision.path} at ${import_picocolors11.default.bold(decision.targetDir)}`);
|
|
2917
3381
|
const cmdName = decision.path === "plugin" ? `/${basename2(decision.targetDir)}:doraval` : "/my-skill";
|
|
2918
3382
|
ui.info(` Command: ${cmdName}`);
|
|
2919
3383
|
if (decision.path === "plugin") {
|
|
@@ -2923,6 +3387,9 @@ var init_new = __esm(() => {
|
|
|
2923
3387
|
}
|
|
2924
3388
|
ui.info(` Test: claude --plugin-dir ${decision.targetDir} (or use normally for standalone)`);
|
|
2925
3389
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
3390
|
+
if (decision.path === "plugin") {
|
|
3391
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
3392
|
+
}
|
|
2926
3393
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
2927
3394
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
2928
3395
|
}
|
|
@@ -2936,8 +3403,8 @@ var exports_bump = {};
|
|
|
2936
3403
|
__export(exports_bump, {
|
|
2937
3404
|
default: () => bump_default
|
|
2938
3405
|
});
|
|
2939
|
-
import { resolve as resolve4, join as
|
|
2940
|
-
import { existsSync as
|
|
3406
|
+
import { resolve as resolve4, join as join11, dirname as dirname3, relative } from "path";
|
|
3407
|
+
import { existsSync as existsSync12, readFileSync, writeFileSync as writeFileSync2, readdirSync as readdirSync5, statSync } from "fs";
|
|
2941
3408
|
function bumpVersion(current, type) {
|
|
2942
3409
|
if (/^\d+\.\d+\.\d+$/.test(type))
|
|
2943
3410
|
return type;
|
|
@@ -2955,7 +3422,7 @@ function bumpVersion(current, type) {
|
|
|
2955
3422
|
throw new Error(`Invalid bump type "${type}". Use patch, minor, major, or an exact version like 1.2.3`);
|
|
2956
3423
|
}
|
|
2957
3424
|
}
|
|
2958
|
-
function
|
|
3425
|
+
function readJson2(p) {
|
|
2959
3426
|
try {
|
|
2960
3427
|
const content = readFileSync(p, "utf8");
|
|
2961
3428
|
return JSON.parse(content);
|
|
@@ -2963,7 +3430,7 @@ function readJson(p) {
|
|
|
2963
3430
|
return null;
|
|
2964
3431
|
}
|
|
2965
3432
|
}
|
|
2966
|
-
function
|
|
3433
|
+
function writeJson2(p, data) {
|
|
2967
3434
|
writeFileSync2(p, JSON.stringify(data, null, 2) + `
|
|
2968
3435
|
`, "utf8");
|
|
2969
3436
|
}
|
|
@@ -3015,12 +3482,12 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3015
3482
|
return results;
|
|
3016
3483
|
let entries;
|
|
3017
3484
|
try {
|
|
3018
|
-
entries =
|
|
3485
|
+
entries = readdirSync5(dir);
|
|
3019
3486
|
} catch {
|
|
3020
3487
|
return results;
|
|
3021
3488
|
}
|
|
3022
3489
|
for (const entry of entries) {
|
|
3023
|
-
const full =
|
|
3490
|
+
const full = join11(dir, entry);
|
|
3024
3491
|
let st;
|
|
3025
3492
|
try {
|
|
3026
3493
|
st = statSync(full);
|
|
@@ -3032,7 +3499,7 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3032
3499
|
results.push(...sub);
|
|
3033
3500
|
} else if (st.isFile()) {
|
|
3034
3501
|
if (entry === "plugin.json") {
|
|
3035
|
-
const parentDir =
|
|
3502
|
+
const parentDir = dirname3(full);
|
|
3036
3503
|
const parentName = parentDir.split(/[/\\]/).pop();
|
|
3037
3504
|
if (parentName === ".claude-plugin" || parentName === ".codex-plugin" || parentName === ".cursor-plugin" || parentName === ".github") {
|
|
3038
3505
|
results.push({
|
|
@@ -3042,7 +3509,7 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3042
3509
|
});
|
|
3043
3510
|
}
|
|
3044
3511
|
} else if (entry === "marketplace.json") {
|
|
3045
|
-
const json =
|
|
3512
|
+
const json = readJson2(full);
|
|
3046
3513
|
if (json && getVersion(json)) {
|
|
3047
3514
|
results.push({
|
|
3048
3515
|
file: full,
|
|
@@ -3055,11 +3522,11 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3055
3522
|
}
|
|
3056
3523
|
return results;
|
|
3057
3524
|
}
|
|
3058
|
-
var
|
|
3525
|
+
var import_picocolors12, bump_default;
|
|
3059
3526
|
var init_bump = __esm(() => {
|
|
3060
3527
|
init_dist();
|
|
3061
3528
|
init_out();
|
|
3062
|
-
|
|
3529
|
+
import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
3063
3530
|
bump_default = defineCommand({
|
|
3064
3531
|
meta: {
|
|
3065
3532
|
name: "bump",
|
|
@@ -3093,7 +3560,7 @@ var init_bump = __esm(() => {
|
|
|
3093
3560
|
}
|
|
3094
3561
|
const isKnownType = ["patch", "minor", "major"].includes(rawType) || /^\d+\.\d+\.\d+$/.test(rawType);
|
|
3095
3562
|
const maybePath = resolve4(rawType);
|
|
3096
|
-
const looksLikeDir =
|
|
3563
|
+
const looksLikeDir = existsSync12(maybePath) || rawType === "." || rawType.startsWith("./") || rawType.startsWith("../");
|
|
3097
3564
|
if (!isKnownType && looksLikeDir) {
|
|
3098
3565
|
targetPath = rawType;
|
|
3099
3566
|
rawType = "patch";
|
|
@@ -3102,7 +3569,7 @@ var init_bump = __esm(() => {
|
|
|
3102
3569
|
process.exit(1);
|
|
3103
3570
|
}
|
|
3104
3571
|
const root = resolve4(targetPath);
|
|
3105
|
-
if (!
|
|
3572
|
+
if (!existsSync12(root)) {
|
|
3106
3573
|
ui.fail(`Path does not exist: ${root}`);
|
|
3107
3574
|
process.exit(1);
|
|
3108
3575
|
}
|
|
@@ -3137,7 +3604,7 @@ var init_bump = __esm(() => {
|
|
|
3137
3604
|
ui.info(` matched ${targets.length} file(s)`);
|
|
3138
3605
|
let bumpedCount = 0;
|
|
3139
3606
|
for (const t of targets) {
|
|
3140
|
-
const json =
|
|
3607
|
+
const json = readJson2(t.file);
|
|
3141
3608
|
if (!json || typeof json !== "object") {
|
|
3142
3609
|
ui.warnItem(`skipped (invalid JSON): ${relative(root, t.file)}`);
|
|
3143
3610
|
continue;
|
|
@@ -3166,11 +3633,11 @@ var init_bump = __esm(() => {
|
|
|
3166
3633
|
ui.warnItem(`skipped (could not locate version field to update): ${relPath}`);
|
|
3167
3634
|
continue;
|
|
3168
3635
|
}
|
|
3169
|
-
|
|
3636
|
+
writeJson2(t.file, json);
|
|
3170
3637
|
if (didRootUpdate && current) {
|
|
3171
|
-
ui.success(`${t.label}: ${
|
|
3638
|
+
ui.success(`${t.label}: ${import_picocolors12.default.dim(current)} \u2192 ${import_picocolors12.default.green(next)}`);
|
|
3172
3639
|
} else if (didRootUpdate) {
|
|
3173
|
-
ui.success(`${t.label}: ${
|
|
3640
|
+
ui.success(`${t.label}: ${import_picocolors12.default.green(next)}`);
|
|
3174
3641
|
} else {
|
|
3175
3642
|
ui.success(`${t.label} (no root version)`);
|
|
3176
3643
|
}
|
|
@@ -3193,16 +3660,16 @@ var init_bump = __esm(() => {
|
|
|
3193
3660
|
});
|
|
3194
3661
|
|
|
3195
3662
|
// src/cli/commands/codex/context.ts
|
|
3196
|
-
import { existsSync as
|
|
3197
|
-
import { join as
|
|
3663
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
|
|
3664
|
+
import { join as join12 } from "path";
|
|
3198
3665
|
function detectContext2(cwd = process.cwd()) {
|
|
3199
3666
|
const codexSpec = getProviderSpec("codex");
|
|
3200
|
-
const hasCodexDir =
|
|
3201
|
-
const hasPluginManifest =
|
|
3202
|
-
const hasMarketplace =
|
|
3667
|
+
const hasCodexDir = existsSync13(join12(cwd, ".codex"));
|
|
3668
|
+
const hasPluginManifest = existsSync13(join12(cwd, codexSpec.manifestPath));
|
|
3669
|
+
const hasMarketplace = existsSync13(join12(cwd, ".agents", "plugins", "marketplace.json")) || existsSync13(join12(cwd, codexSpec.manifestPath));
|
|
3203
3670
|
let looseSkillFiles = [];
|
|
3204
3671
|
try {
|
|
3205
|
-
const files =
|
|
3672
|
+
const files = readdirSync6(cwd);
|
|
3206
3673
|
looseSkillFiles = files.filter((f) => {
|
|
3207
3674
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3208
3675
|
return false;
|
|
@@ -3222,7 +3689,7 @@ function detectContext2(cwd = process.cwd()) {
|
|
|
3222
3689
|
isEmpty
|
|
3223
3690
|
};
|
|
3224
3691
|
}
|
|
3225
|
-
var
|
|
3692
|
+
var init_context3 = __esm(() => {
|
|
3226
3693
|
init_spec();
|
|
3227
3694
|
});
|
|
3228
3695
|
|
|
@@ -3233,8 +3700,8 @@ __export(exports_new2, {
|
|
|
3233
3700
|
default: () => new_default2,
|
|
3234
3701
|
decidePath: () => decidePath2
|
|
3235
3702
|
});
|
|
3236
|
-
import { join as
|
|
3237
|
-
import { mkdirSync as
|
|
3703
|
+
import { join as join13, basename as basename3, dirname as dirname4 } from "path";
|
|
3704
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, existsSync as existsSync14 } from "fs";
|
|
3238
3705
|
function decidePath2(ctx, intent, providedName) {
|
|
3239
3706
|
const rawName = providedName || "";
|
|
3240
3707
|
let decisionPath = "standalone";
|
|
@@ -3248,7 +3715,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3248
3715
|
targetDir = ctx.cwd;
|
|
3249
3716
|
shouldCreateDir = false;
|
|
3250
3717
|
} else {
|
|
3251
|
-
targetDir =
|
|
3718
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3252
3719
|
shouldCreateDir = true;
|
|
3253
3720
|
}
|
|
3254
3721
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3258,7 +3725,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3258
3725
|
targetDir = ctx.cwd;
|
|
3259
3726
|
shouldCreateDir = false;
|
|
3260
3727
|
} else {
|
|
3261
|
-
targetDir =
|
|
3728
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3262
3729
|
shouldCreateDir = true;
|
|
3263
3730
|
}
|
|
3264
3731
|
} else if (decisionPath === "standalone") {
|
|
@@ -3266,7 +3733,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3266
3733
|
targetDir = ctx.cwd;
|
|
3267
3734
|
shouldCreateDir = false;
|
|
3268
3735
|
} else {
|
|
3269
|
-
targetDir =
|
|
3736
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3270
3737
|
shouldCreateDir = true;
|
|
3271
3738
|
}
|
|
3272
3739
|
}
|
|
@@ -3274,17 +3741,17 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3274
3741
|
}
|
|
3275
3742
|
function scaffold2(decision, ctx, migrateContent) {
|
|
3276
3743
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3277
|
-
if (
|
|
3744
|
+
if (existsSync14(targetDir) && shouldCreateDir) {
|
|
3278
3745
|
ui.fail("Target already exists");
|
|
3279
3746
|
process.exit(1);
|
|
3280
3747
|
}
|
|
3281
3748
|
if (shouldCreateDir) {
|
|
3282
|
-
|
|
3749
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
3283
3750
|
}
|
|
3284
3751
|
if (path === "plugin") {
|
|
3285
3752
|
const pluginName = basename3(targetDir);
|
|
3286
3753
|
const codexSpec = getProviderSpec("codex");
|
|
3287
|
-
const codexManifestDir =
|
|
3754
|
+
const codexManifestDir = dirname4(codexSpec.manifestPath);
|
|
3288
3755
|
const pluginJson = {
|
|
3289
3756
|
name: pluginName,
|
|
3290
3757
|
version: "0.1.0",
|
|
@@ -3294,12 +3761,13 @@ function scaffold2(decision, ctx, migrateContent) {
|
|
|
3294
3761
|
displayName: pluginName,
|
|
3295
3762
|
shortDescription: "Scaffolded starter plugin",
|
|
3296
3763
|
category: "Productivity"
|
|
3297
|
-
}
|
|
3764
|
+
},
|
|
3765
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3298
3766
|
};
|
|
3299
|
-
|
|
3300
|
-
writeFileSync3(
|
|
3301
|
-
const marketplaceDir =
|
|
3302
|
-
|
|
3767
|
+
mkdirSync4(join13(targetDir, codexManifestDir), { recursive: true });
|
|
3768
|
+
writeFileSync3(join13(targetDir, codexSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
3769
|
+
const marketplaceDir = dirname4(codexSpec.marketplacePath);
|
|
3770
|
+
mkdirSync4(join13(targetDir, marketplaceDir), { recursive: true });
|
|
3303
3771
|
const marketplaceJson = {
|
|
3304
3772
|
name: "local",
|
|
3305
3773
|
interface: {
|
|
@@ -3320,9 +3788,9 @@ function scaffold2(decision, ctx, migrateContent) {
|
|
|
3320
3788
|
}
|
|
3321
3789
|
]
|
|
3322
3790
|
};
|
|
3323
|
-
writeFileSync3(
|
|
3791
|
+
writeFileSync3(join13(targetDir, codexSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3324
3792
|
const demoSkillName = "doraval";
|
|
3325
|
-
|
|
3793
|
+
mkdirSync4(join13(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3326
3794
|
let skillContent;
|
|
3327
3795
|
if (migrateContent) {
|
|
3328
3796
|
skillContent = migrateContent;
|
|
@@ -3353,19 +3821,19 @@ To test in Codex:
|
|
|
3353
3821
|
3. Open the plugin directory, select your local marketplace, and enable the plugin.
|
|
3354
3822
|
4. Invoke the demo with /${pluginName}:doraval`;
|
|
3355
3823
|
}
|
|
3356
|
-
writeFileSync3(
|
|
3357
|
-
const readmePath =
|
|
3358
|
-
if (!
|
|
3824
|
+
writeFileSync3(join13(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
3825
|
+
const readmePath = join13(targetDir, "README.md");
|
|
3826
|
+
if (!existsSync14(readmePath)) {
|
|
3359
3827
|
writeFileSync3(readmePath, "# " + pluginName + `
|
|
3360
3828
|
|
|
3361
3829
|
Codex plugin scaffolded by doraval.`);
|
|
3362
3830
|
}
|
|
3363
3831
|
} else {
|
|
3364
|
-
|
|
3832
|
+
mkdirSync4(join13(targetDir, "skills", "doraval"), { recursive: true });
|
|
3365
3833
|
const skillBody = migrateContent || `# My Skill
|
|
3366
3834
|
|
|
3367
3835
|
Basic starter for Codex.`;
|
|
3368
|
-
writeFileSync3(
|
|
3836
|
+
writeFileSync3(join13(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3369
3837
|
name: doraval
|
|
3370
3838
|
description: Starter (local skill)
|
|
3371
3839
|
---
|
|
@@ -3373,14 +3841,14 @@ description: Starter (local skill)
|
|
|
3373
3841
|
${skillBody}`);
|
|
3374
3842
|
}
|
|
3375
3843
|
}
|
|
3376
|
-
var
|
|
3844
|
+
var import_picocolors13, new_default2;
|
|
3377
3845
|
var init_new2 = __esm(() => {
|
|
3378
3846
|
init_dist();
|
|
3379
3847
|
init_out();
|
|
3380
|
-
|
|
3848
|
+
init_context3();
|
|
3381
3849
|
init_prompt();
|
|
3382
3850
|
init_spec();
|
|
3383
|
-
|
|
3851
|
+
import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
3384
3852
|
new_default2 = defineCommand({
|
|
3385
3853
|
meta: {
|
|
3386
3854
|
name: "new",
|
|
@@ -3419,7 +3887,7 @@ var init_new2 = __esm(() => {
|
|
|
3419
3887
|
}
|
|
3420
3888
|
scaffold2(decision, ctx, migrateContent);
|
|
3421
3889
|
ui.write(`
|
|
3422
|
-
${
|
|
3890
|
+
${import_picocolors13.default.green("\u2713")} Created ${decision.path} at ${import_picocolors13.default.bold(decision.targetDir)}`);
|
|
3423
3891
|
const cmdName = decision.path === "plugin" ? `/${basename3(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3424
3892
|
ui.info(` Command: ${cmdName}`);
|
|
3425
3893
|
if (decision.path === "plugin") {
|
|
@@ -3429,6 +3897,9 @@ var init_new2 = __esm(() => {
|
|
|
3429
3897
|
}
|
|
3430
3898
|
ui.info(` Test (local): restart Codex, select your marketplace in the plugin directory`);
|
|
3431
3899
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
3900
|
+
if (decision.path === "plugin") {
|
|
3901
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
3902
|
+
}
|
|
3432
3903
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3433
3904
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3434
3905
|
}
|
|
@@ -3438,14 +3909,14 @@ var init_new2 = __esm(() => {
|
|
|
3438
3909
|
});
|
|
3439
3910
|
|
|
3440
3911
|
// src/cli/commands/cursor/context.ts
|
|
3441
|
-
import { existsSync as
|
|
3442
|
-
import { join as
|
|
3912
|
+
import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
|
|
3913
|
+
import { join as join14 } from "path";
|
|
3443
3914
|
function detectContext3(cwd = process.cwd()) {
|
|
3444
|
-
const hasCursorDir =
|
|
3445
|
-
const hasPluginManifest =
|
|
3915
|
+
const hasCursorDir = existsSync15(join14(cwd, ".cursor"));
|
|
3916
|
+
const hasPluginManifest = existsSync15(join14(cwd, ".cursor-plugin", "plugin.json"));
|
|
3446
3917
|
let looseSkillFiles = [];
|
|
3447
3918
|
try {
|
|
3448
|
-
const files =
|
|
3919
|
+
const files = readdirSync7(cwd);
|
|
3449
3920
|
looseSkillFiles = files.filter((f) => {
|
|
3450
3921
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3451
3922
|
return false;
|
|
@@ -3464,7 +3935,7 @@ function detectContext3(cwd = process.cwd()) {
|
|
|
3464
3935
|
isEmpty
|
|
3465
3936
|
};
|
|
3466
3937
|
}
|
|
3467
|
-
var
|
|
3938
|
+
var init_context4 = () => {};
|
|
3468
3939
|
|
|
3469
3940
|
// src/cli/commands/cursor/new.ts
|
|
3470
3941
|
var exports_new3 = {};
|
|
@@ -3473,8 +3944,8 @@ __export(exports_new3, {
|
|
|
3473
3944
|
default: () => new_default3,
|
|
3474
3945
|
decidePath: () => decidePath3
|
|
3475
3946
|
});
|
|
3476
|
-
import { join as
|
|
3477
|
-
import { mkdirSync as
|
|
3947
|
+
import { join as join15, basename as basename4, dirname as dirname5 } from "path";
|
|
3948
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, existsSync as existsSync16 } from "fs";
|
|
3478
3949
|
function decidePath3(ctx, intent, providedName) {
|
|
3479
3950
|
const rawName = providedName || "";
|
|
3480
3951
|
let decisionPath = "standalone";
|
|
@@ -3488,7 +3959,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3488
3959
|
targetDir = ctx.cwd;
|
|
3489
3960
|
shouldCreateDir = false;
|
|
3490
3961
|
} else {
|
|
3491
|
-
targetDir =
|
|
3962
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3492
3963
|
shouldCreateDir = true;
|
|
3493
3964
|
}
|
|
3494
3965
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3498,7 +3969,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3498
3969
|
targetDir = ctx.cwd;
|
|
3499
3970
|
shouldCreateDir = false;
|
|
3500
3971
|
} else {
|
|
3501
|
-
targetDir =
|
|
3972
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3502
3973
|
shouldCreateDir = true;
|
|
3503
3974
|
}
|
|
3504
3975
|
} else if (decisionPath === "standalone") {
|
|
@@ -3506,7 +3977,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3506
3977
|
targetDir = ctx.cwd;
|
|
3507
3978
|
shouldCreateDir = false;
|
|
3508
3979
|
} else {
|
|
3509
|
-
targetDir =
|
|
3980
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3510
3981
|
shouldCreateDir = true;
|
|
3511
3982
|
}
|
|
3512
3983
|
}
|
|
@@ -3514,28 +3985,29 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3514
3985
|
}
|
|
3515
3986
|
function scaffold3(decision, ctx, migrateContent) {
|
|
3516
3987
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3517
|
-
if (
|
|
3988
|
+
if (existsSync16(targetDir) && shouldCreateDir) {
|
|
3518
3989
|
ui.fail("Target already exists");
|
|
3519
3990
|
process.exit(1);
|
|
3520
3991
|
}
|
|
3521
3992
|
if (shouldCreateDir) {
|
|
3522
|
-
|
|
3993
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
3523
3994
|
}
|
|
3524
3995
|
if (path === "plugin") {
|
|
3525
3996
|
const pluginName = basename4(targetDir);
|
|
3526
3997
|
const cursorSpec = getProviderSpec("cursor");
|
|
3527
|
-
const cursorManifestDir =
|
|
3998
|
+
const cursorManifestDir = dirname5(cursorSpec.manifestPath);
|
|
3528
3999
|
const pluginJson = {
|
|
3529
4000
|
name: pluginName,
|
|
3530
4001
|
version: "0.1.0",
|
|
3531
4002
|
description: "Scaffolded by doraval cursor new",
|
|
3532
4003
|
skills: "./skills/",
|
|
3533
|
-
displayName: pluginName
|
|
4004
|
+
displayName: pluginName,
|
|
4005
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3534
4006
|
};
|
|
3535
|
-
|
|
3536
|
-
writeFileSync4(
|
|
3537
|
-
const marketplaceDir =
|
|
3538
|
-
|
|
4007
|
+
mkdirSync5(join15(targetDir, cursorManifestDir), { recursive: true });
|
|
4008
|
+
writeFileSync4(join15(targetDir, cursorSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
4009
|
+
const marketplaceDir = dirname5(cursorSpec.marketplacePath);
|
|
4010
|
+
mkdirSync5(join15(targetDir, marketplaceDir), { recursive: true });
|
|
3539
4011
|
const marketplaceJson = {
|
|
3540
4012
|
name: pluginName,
|
|
3541
4013
|
version: "0.1.0",
|
|
@@ -3546,9 +4018,9 @@ function scaffold3(decision, ctx, migrateContent) {
|
|
|
3546
4018
|
license: "MIT",
|
|
3547
4019
|
keywords: ["cursor", "skills", "plugin"]
|
|
3548
4020
|
};
|
|
3549
|
-
writeFileSync4(
|
|
4021
|
+
writeFileSync4(join15(targetDir, cursorSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3550
4022
|
const demoSkillName = "doraval";
|
|
3551
|
-
|
|
4023
|
+
mkdirSync5(join15(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3552
4024
|
let skillContent;
|
|
3553
4025
|
if (migrateContent) {
|
|
3554
4026
|
skillContent = migrateContent;
|
|
@@ -3577,19 +4049,19 @@ To test in Cursor:
|
|
|
3577
4049
|
1. Open the plugin directory or add via marketplace.
|
|
3578
4050
|
2. The demo skill will be available.`;
|
|
3579
4051
|
}
|
|
3580
|
-
writeFileSync4(
|
|
3581
|
-
const readmePath =
|
|
3582
|
-
if (!
|
|
4052
|
+
writeFileSync4(join15(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
4053
|
+
const readmePath = join15(targetDir, "README.md");
|
|
4054
|
+
if (!existsSync16(readmePath)) {
|
|
3583
4055
|
writeFileSync4(readmePath, "# " + pluginName + `
|
|
3584
4056
|
|
|
3585
4057
|
Cursor plugin scaffolded by doraval.`);
|
|
3586
4058
|
}
|
|
3587
4059
|
} else {
|
|
3588
|
-
|
|
4060
|
+
mkdirSync5(join15(targetDir, "skills", "doraval"), { recursive: true });
|
|
3589
4061
|
const skillBody = migrateContent || `# My Skill
|
|
3590
4062
|
|
|
3591
4063
|
Basic starter for Cursor.`;
|
|
3592
|
-
writeFileSync4(
|
|
4064
|
+
writeFileSync4(join15(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3593
4065
|
name: doraval
|
|
3594
4066
|
description: Starter (local skill)
|
|
3595
4067
|
---
|
|
@@ -3597,14 +4069,14 @@ description: Starter (local skill)
|
|
|
3597
4069
|
${skillBody}`);
|
|
3598
4070
|
}
|
|
3599
4071
|
}
|
|
3600
|
-
var
|
|
4072
|
+
var import_picocolors14, new_default3;
|
|
3601
4073
|
var init_new3 = __esm(() => {
|
|
3602
4074
|
init_dist();
|
|
3603
4075
|
init_out();
|
|
3604
|
-
|
|
4076
|
+
init_context4();
|
|
3605
4077
|
init_prompt();
|
|
3606
4078
|
init_spec();
|
|
3607
|
-
|
|
4079
|
+
import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
3608
4080
|
new_default3 = defineCommand({
|
|
3609
4081
|
meta: {
|
|
3610
4082
|
name: "new",
|
|
@@ -3643,7 +4115,7 @@ var init_new3 = __esm(() => {
|
|
|
3643
4115
|
}
|
|
3644
4116
|
scaffold3(decision, ctx, migrateContent);
|
|
3645
4117
|
ui.write(`
|
|
3646
|
-
${
|
|
4118
|
+
${import_picocolors14.default.green("\u2713")} Created ${decision.path} at ${import_picocolors14.default.bold(decision.targetDir)}`);
|
|
3647
4119
|
const cmdName = decision.path === "plugin" ? `/${basename4(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3648
4120
|
ui.info(` Command: ${cmdName}`);
|
|
3649
4121
|
if (decision.path === "plugin") {
|
|
@@ -3652,6 +4124,9 @@ var init_new3 = __esm(() => {
|
|
|
3652
4124
|
}
|
|
3653
4125
|
ui.info(` Test (local): add the plugin dir in Cursor settings or use local skills`);
|
|
3654
4126
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
4127
|
+
if (decision.path === "plugin") {
|
|
4128
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
4129
|
+
}
|
|
3655
4130
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3656
4131
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3657
4132
|
}
|
|
@@ -3661,14 +4136,14 @@ var init_new3 = __esm(() => {
|
|
|
3661
4136
|
});
|
|
3662
4137
|
|
|
3663
4138
|
// src/cli/commands/copilot/context.ts
|
|
3664
|
-
import { existsSync as
|
|
3665
|
-
import { join as
|
|
4139
|
+
import { existsSync as existsSync17, readdirSync as readdirSync8 } from "fs";
|
|
4140
|
+
import { join as join16 } from "path";
|
|
3666
4141
|
function detectContext4(cwd = process.cwd()) {
|
|
3667
|
-
const hasGithubDir =
|
|
3668
|
-
const hasPluginManifest =
|
|
4142
|
+
const hasGithubDir = existsSync17(join16(cwd, ".github"));
|
|
4143
|
+
const hasPluginManifest = existsSync17(join16(cwd, ".github", "plugin", "plugin.json"));
|
|
3669
4144
|
let looseSkillFiles = [];
|
|
3670
4145
|
try {
|
|
3671
|
-
const files =
|
|
4146
|
+
const files = readdirSync8(cwd);
|
|
3672
4147
|
looseSkillFiles = files.filter((f) => {
|
|
3673
4148
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3674
4149
|
return false;
|
|
@@ -3687,7 +4162,7 @@ function detectContext4(cwd = process.cwd()) {
|
|
|
3687
4162
|
isEmpty
|
|
3688
4163
|
};
|
|
3689
4164
|
}
|
|
3690
|
-
var
|
|
4165
|
+
var init_context5 = () => {};
|
|
3691
4166
|
|
|
3692
4167
|
// src/cli/commands/copilot/new.ts
|
|
3693
4168
|
var exports_new4 = {};
|
|
@@ -3696,8 +4171,8 @@ __export(exports_new4, {
|
|
|
3696
4171
|
default: () => new_default4,
|
|
3697
4172
|
decidePath: () => decidePath4
|
|
3698
4173
|
});
|
|
3699
|
-
import { join as
|
|
3700
|
-
import { mkdirSync as
|
|
4174
|
+
import { join as join17, basename as basename5, dirname as dirname6 } from "path";
|
|
4175
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "fs";
|
|
3701
4176
|
function decidePath4(ctx, intent, providedName) {
|
|
3702
4177
|
const rawName = providedName || "";
|
|
3703
4178
|
let decisionPath = "standalone";
|
|
@@ -3711,7 +4186,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3711
4186
|
targetDir = ctx.cwd;
|
|
3712
4187
|
shouldCreateDir = false;
|
|
3713
4188
|
} else {
|
|
3714
|
-
targetDir =
|
|
4189
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3715
4190
|
shouldCreateDir = true;
|
|
3716
4191
|
}
|
|
3717
4192
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3721,7 +4196,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3721
4196
|
targetDir = ctx.cwd;
|
|
3722
4197
|
shouldCreateDir = false;
|
|
3723
4198
|
} else {
|
|
3724
|
-
targetDir =
|
|
4199
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3725
4200
|
shouldCreateDir = true;
|
|
3726
4201
|
}
|
|
3727
4202
|
} else if (decisionPath === "standalone") {
|
|
@@ -3729,7 +4204,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3729
4204
|
targetDir = ctx.cwd;
|
|
3730
4205
|
shouldCreateDir = false;
|
|
3731
4206
|
} else {
|
|
3732
|
-
targetDir =
|
|
4207
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3733
4208
|
shouldCreateDir = true;
|
|
3734
4209
|
}
|
|
3735
4210
|
}
|
|
@@ -3737,28 +4212,29 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3737
4212
|
}
|
|
3738
4213
|
function scaffold4(decision, ctx, migrateContent) {
|
|
3739
4214
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3740
|
-
if (
|
|
4215
|
+
if (existsSync18(targetDir) && shouldCreateDir) {
|
|
3741
4216
|
ui.fail("Target already exists");
|
|
3742
4217
|
process.exit(1);
|
|
3743
4218
|
}
|
|
3744
4219
|
if (shouldCreateDir) {
|
|
3745
|
-
|
|
4220
|
+
mkdirSync6(targetDir, { recursive: true });
|
|
3746
4221
|
}
|
|
3747
4222
|
if (path === "plugin") {
|
|
3748
4223
|
const pluginName = basename5(targetDir);
|
|
3749
4224
|
const copilotSpec = getProviderSpec("copilot");
|
|
3750
|
-
const copilotManifestDir =
|
|
4225
|
+
const copilotManifestDir = dirname6(copilotSpec.manifestPath);
|
|
3751
4226
|
const pluginJson = {
|
|
3752
4227
|
name: pluginName,
|
|
3753
4228
|
version: "0.1.0",
|
|
3754
4229
|
description: "Scaffolded by doraval copilot new",
|
|
3755
4230
|
skills: ["./skills/doraval"],
|
|
3756
|
-
displayName: pluginName
|
|
4231
|
+
displayName: pluginName,
|
|
4232
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3757
4233
|
};
|
|
3758
|
-
|
|
3759
|
-
writeFileSync5(
|
|
3760
|
-
const marketplaceDir =
|
|
3761
|
-
|
|
4234
|
+
mkdirSync6(join17(targetDir, copilotManifestDir), { recursive: true });
|
|
4235
|
+
writeFileSync5(join17(targetDir, copilotSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
4236
|
+
const marketplaceDir = dirname6(copilotSpec.marketplacePath);
|
|
4237
|
+
mkdirSync6(join17(targetDir, marketplaceDir), { recursive: true });
|
|
3762
4238
|
const marketplaceJson = {
|
|
3763
4239
|
name: "local",
|
|
3764
4240
|
plugins: [
|
|
@@ -3771,9 +4247,9 @@ function scaffold4(decision, ctx, migrateContent) {
|
|
|
3771
4247
|
}
|
|
3772
4248
|
]
|
|
3773
4249
|
};
|
|
3774
|
-
writeFileSync5(
|
|
4250
|
+
writeFileSync5(join17(targetDir, copilotSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3775
4251
|
const demoSkillName = "doraval";
|
|
3776
|
-
|
|
4252
|
+
mkdirSync6(join17(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3777
4253
|
let skillContent;
|
|
3778
4254
|
if (migrateContent) {
|
|
3779
4255
|
skillContent = migrateContent;
|
|
@@ -3802,19 +4278,19 @@ To test in Copilot:
|
|
|
3802
4278
|
1. Configure the .github/plugin as local source.
|
|
3803
4279
|
2. Restart/reload and invoke the skill.`;
|
|
3804
4280
|
}
|
|
3805
|
-
writeFileSync5(
|
|
3806
|
-
const readmePath =
|
|
3807
|
-
if (!
|
|
4281
|
+
writeFileSync5(join17(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
4282
|
+
const readmePath = join17(targetDir, "README.md");
|
|
4283
|
+
if (!existsSync18(readmePath)) {
|
|
3808
4284
|
writeFileSync5(readmePath, "# " + pluginName + `
|
|
3809
4285
|
|
|
3810
4286
|
Copilot plugin scaffolded by doraval.`);
|
|
3811
4287
|
}
|
|
3812
4288
|
} else {
|
|
3813
|
-
|
|
4289
|
+
mkdirSync6(join17(targetDir, "skills", "doraval"), { recursive: true });
|
|
3814
4290
|
const skillBody = migrateContent || `# My Skill
|
|
3815
4291
|
|
|
3816
4292
|
Basic starter for Copilot.`;
|
|
3817
|
-
writeFileSync5(
|
|
4293
|
+
writeFileSync5(join17(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3818
4294
|
name: doraval
|
|
3819
4295
|
description: Starter (local skill)
|
|
3820
4296
|
---
|
|
@@ -3822,14 +4298,14 @@ description: Starter (local skill)
|
|
|
3822
4298
|
${skillBody}`);
|
|
3823
4299
|
}
|
|
3824
4300
|
}
|
|
3825
|
-
var
|
|
4301
|
+
var import_picocolors15, new_default4;
|
|
3826
4302
|
var init_new4 = __esm(() => {
|
|
3827
4303
|
init_dist();
|
|
3828
4304
|
init_out();
|
|
3829
|
-
|
|
4305
|
+
init_context5();
|
|
3830
4306
|
init_prompt();
|
|
3831
4307
|
init_spec();
|
|
3832
|
-
|
|
4308
|
+
import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
3833
4309
|
new_default4 = defineCommand({
|
|
3834
4310
|
meta: {
|
|
3835
4311
|
name: "new",
|
|
@@ -3868,7 +4344,7 @@ var init_new4 = __esm(() => {
|
|
|
3868
4344
|
}
|
|
3869
4345
|
scaffold4(decision, ctx, migrateContent);
|
|
3870
4346
|
ui.write(`
|
|
3871
|
-
${
|
|
4347
|
+
${import_picocolors15.default.green("\u2713")} Created ${decision.path} at ${import_picocolors15.default.bold(decision.targetDir)}`);
|
|
3872
4348
|
const cmdName = decision.path === "plugin" ? `/${basename5(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3873
4349
|
ui.info(` Command: ${cmdName}`);
|
|
3874
4350
|
if (decision.path === "plugin") {
|
|
@@ -3877,6 +4353,9 @@ var init_new4 = __esm(() => {
|
|
|
3877
4353
|
}
|
|
3878
4354
|
ui.info(` Test (local): configure local plugin source in Copilot and reload`);
|
|
3879
4355
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
4356
|
+
if (decision.path === "plugin") {
|
|
4357
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
4358
|
+
}
|
|
3880
4359
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3881
4360
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3882
4361
|
}
|
|
@@ -3885,8 +4364,240 @@ var init_new4 = __esm(() => {
|
|
|
3885
4364
|
});
|
|
3886
4365
|
});
|
|
3887
4366
|
|
|
4367
|
+
// src/cli/commands/ui.ts
|
|
4368
|
+
var exports_ui = {};
|
|
4369
|
+
__export(exports_ui, {
|
|
4370
|
+
default: () => ui_default
|
|
4371
|
+
});
|
|
4372
|
+
import { existsSync as existsSync19, readdirSync as readdirSync9 } from "fs";
|
|
4373
|
+
import { join as join18 } from "path";
|
|
4374
|
+
import { spawn } from "child_process";
|
|
4375
|
+
function slugify2(title) {
|
|
4376
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
|
|
4377
|
+
}
|
|
4378
|
+
async function loadAllEntries(project) {
|
|
4379
|
+
const journalsDir = getJournalsDir();
|
|
4380
|
+
const entries = [];
|
|
4381
|
+
const globalPath = join18(journalsDir, "global.md");
|
|
4382
|
+
if (existsSync19(globalPath)) {
|
|
4383
|
+
try {
|
|
4384
|
+
const raw = await Bun.file(globalPath).text();
|
|
4385
|
+
const parsed = parseJournalEntries(raw);
|
|
4386
|
+
parsed.forEach((e) => entries.push({ ...e, _source: "global" }));
|
|
4387
|
+
} catch {}
|
|
4388
|
+
}
|
|
4389
|
+
if (project) {
|
|
4390
|
+
const projPath = join18(journalsDir, `${project}.md`);
|
|
4391
|
+
if (existsSync19(projPath)) {
|
|
4392
|
+
try {
|
|
4393
|
+
const raw = await Bun.file(projPath).text();
|
|
4394
|
+
const parsed = parseJournalEntries(raw);
|
|
4395
|
+
parsed.forEach((e) => entries.push({ ...e, _source: "project" }));
|
|
4396
|
+
} catch {}
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
const staged = [];
|
|
4400
|
+
try {
|
|
4401
|
+
const pdir = project ? getPendingProjectDir(project) : null;
|
|
4402
|
+
if (pdir && existsSync19(pdir)) {
|
|
4403
|
+
const files = readdirSync9(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
|
|
4404
|
+
for (const f of files) {
|
|
4405
|
+
const txt = await Bun.file(join18(pdir, f)).text();
|
|
4406
|
+
const parsed = parseJournalEntries(txt);
|
|
4407
|
+
parsed.forEach((e) => {
|
|
4408
|
+
e._staged = true;
|
|
4409
|
+
e._source = "staged";
|
|
4410
|
+
staged.push(e);
|
|
4411
|
+
});
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
} catch {}
|
|
4415
|
+
return { committed: entries, staged };
|
|
4416
|
+
}
|
|
4417
|
+
async function writePendingEntry(project, input) {
|
|
4418
|
+
ensureDoravalDirs();
|
|
4419
|
+
const pendingDir = getPendingProjectDir(project);
|
|
4420
|
+
if (!existsSync19(pendingDir)) {
|
|
4421
|
+
await Bun.write(join18(pendingDir, ".gitkeep"), "");
|
|
4422
|
+
}
|
|
4423
|
+
const date = new Date().toISOString().split("T")[0];
|
|
4424
|
+
const slug = slugify2(input.title);
|
|
4425
|
+
const filename = `${date}-${slug}.md`;
|
|
4426
|
+
const filePath = join18(pendingDir, filename);
|
|
4427
|
+
const content = `## ${input.title}
|
|
4428
|
+
|
|
4429
|
+
\`\`\`yaml
|
|
4430
|
+
pushback: ${input.pushback}
|
|
4431
|
+
tags: [${input.tags.join(", ")}]
|
|
4432
|
+
author: ${input.author || "human"}
|
|
4433
|
+
date: ${date}
|
|
4434
|
+
status: active
|
|
4435
|
+
\`\`\`
|
|
4436
|
+
|
|
4437
|
+
${input.rationale}
|
|
4438
|
+
`;
|
|
4439
|
+
await Bun.write(filePath, content);
|
|
4440
|
+
return { filePath, filename };
|
|
4441
|
+
}
|
|
4442
|
+
async function killPort(port) {
|
|
4443
|
+
if (process.platform === "win32") {
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4446
|
+
try {
|
|
4447
|
+
const proc = Bun.spawn(["lsof", "-ti", `tcp:${port}`, "-sTCP:LISTEN"], { stdout: "pipe", stderr: "ignore" });
|
|
4448
|
+
const output = (await new Response(proc.stdout).text()).trim();
|
|
4449
|
+
if (!output)
|
|
4450
|
+
return;
|
|
4451
|
+
const pids = output.split(`
|
|
4452
|
+
`).map((p) => p.trim()).filter(Boolean);
|
|
4453
|
+
console.error(` Killing previous doraval ui on port ${port}...`);
|
|
4454
|
+
for (const pid of pids) {
|
|
4455
|
+
console.error(` \u2192 kill -9 ${pid}`);
|
|
4456
|
+
Bun.spawn(["kill", "-9", pid], { stdout: "ignore", stderr: "ignore" });
|
|
4457
|
+
}
|
|
4458
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
4459
|
+
} catch {}
|
|
4460
|
+
}
|
|
4461
|
+
async function getDashboardHtml() {
|
|
4462
|
+
const isSource = import.meta.url.includes("/src/");
|
|
4463
|
+
const htmlPath = isSource ? new URL("../../ui/index.html", import.meta.url) : new URL("./ui/index.html", import.meta.url);
|
|
4464
|
+
try {
|
|
4465
|
+
return await Bun.file(htmlPath).text();
|
|
4466
|
+
} catch (err) {
|
|
4467
|
+
console.error(`[doraval ui] Failed to load HTML from ${htmlPath}`);
|
|
4468
|
+
return `<!doctype html><meta charset="utf-8"><body style="font-family:monospace;background:#111;color:#ddd;padding:2rem"><h1>doraval ui</h1><p>Dashboard HTML missing.</p><pre>${String(err)}</pre></body>`;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
var import_picocolors16, DEFAULT_PORT = 3737, ui_default;
|
|
4472
|
+
var init_ui = __esm(() => {
|
|
4473
|
+
init_journal_config();
|
|
4474
|
+
init_journal_parse();
|
|
4475
|
+
init_context();
|
|
4476
|
+
init_hook();
|
|
4477
|
+
import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
4478
|
+
ui_default = {
|
|
4479
|
+
async run({ args }) {
|
|
4480
|
+
const port = Number(args.port) || DEFAULT_PORT;
|
|
4481
|
+
const host = args.host || "127.0.0.1";
|
|
4482
|
+
const shouldOpen = args.open !== false;
|
|
4483
|
+
await killPort(port);
|
|
4484
|
+
const config = await readConfig();
|
|
4485
|
+
let project = resolveProjectName(config) ?? undefined;
|
|
4486
|
+
if (project) {
|
|
4487
|
+
try {
|
|
4488
|
+
project = sanitizeProjectName(project);
|
|
4489
|
+
} catch {
|
|
4490
|
+
project = undefined;
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
const server = Bun.serve({
|
|
4494
|
+
port,
|
|
4495
|
+
hostname: host,
|
|
4496
|
+
async fetch(req) {
|
|
4497
|
+
const url2 = new URL(req.url);
|
|
4498
|
+
if (url2.pathname === "/" || url2.pathname === "/index.html") {
|
|
4499
|
+
const html = await getDashboardHtml();
|
|
4500
|
+
return new Response(html, {
|
|
4501
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
4502
|
+
});
|
|
4503
|
+
}
|
|
4504
|
+
if (url2.pathname === "/api/status") {
|
|
4505
|
+
return Response.json({
|
|
4506
|
+
project: project || null,
|
|
4507
|
+
doravalDir: getJournalsDir(),
|
|
4508
|
+
hasConfig: !!config,
|
|
4509
|
+
repo: config?.journal?.repo ?? null
|
|
4510
|
+
});
|
|
4511
|
+
}
|
|
4512
|
+
if (url2.pathname === "/api/entries") {
|
|
4513
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4514
|
+
return Response.json({ project, committed, staged });
|
|
4515
|
+
}
|
|
4516
|
+
if (url2.pathname === "/api/context") {
|
|
4517
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4518
|
+
const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
|
|
4519
|
+
const text = generateJournalContext(all, project || null, { minPushback: 1 });
|
|
4520
|
+
return Response.json({ text, project });
|
|
4521
|
+
}
|
|
4522
|
+
if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
|
|
4523
|
+
const localPath = getLocalHooksPath();
|
|
4524
|
+
const globalPath = getGlobalSettingsPath();
|
|
4525
|
+
const localHas = hasHook(await readJson(localPath));
|
|
4526
|
+
const globalHas = hasHook(await readJson(globalPath));
|
|
4527
|
+
return Response.json({
|
|
4528
|
+
local: { enabled: localHas, path: localPath },
|
|
4529
|
+
global: { enabled: globalHas, path: globalPath }
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
|
|
4533
|
+
const body = await req.json().catch(() => ({}));
|
|
4534
|
+
const useGlobal = !!body.global;
|
|
4535
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4536
|
+
const res = await addHook(target);
|
|
4537
|
+
return Response.json(res);
|
|
4538
|
+
}
|
|
4539
|
+
if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
|
|
4540
|
+
const body = await req.json().catch(() => ({}));
|
|
4541
|
+
const useGlobal = !!body.global;
|
|
4542
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4543
|
+
const res = await removeHook(target);
|
|
4544
|
+
return Response.json(res);
|
|
4545
|
+
}
|
|
4546
|
+
if (url2.pathname === "/api/add" && req.method === "POST") {
|
|
4547
|
+
if (!project) {
|
|
4548
|
+
return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
|
|
4549
|
+
}
|
|
4550
|
+
const body = await req.json();
|
|
4551
|
+
const title = String(body.title || "Untitled decision").trim();
|
|
4552
|
+
const pushback = Number(body.pushback ?? 4);
|
|
4553
|
+
const tags = Array.isArray(body.tags) ? body.tags.map((t) => String(t).trim()).filter(Boolean) : [];
|
|
4554
|
+
const rationale = String(body.rationale || title).trim();
|
|
4555
|
+
try {
|
|
4556
|
+
const result = await writePendingEntry(project, { title, pushback, tags, rationale });
|
|
4557
|
+
return Response.json({ ok: true, ...result });
|
|
4558
|
+
} catch (e) {
|
|
4559
|
+
return Response.json({ error: e.message }, { status: 500 });
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
if (url2.pathname === "/api/refresh" && req.method === "POST") {
|
|
4563
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4564
|
+
return Response.json({ ok: true, committed, staged });
|
|
4565
|
+
}
|
|
4566
|
+
if (url2.pathname.startsWith("/api/")) {
|
|
4567
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
4568
|
+
}
|
|
4569
|
+
return new Response("Not found", { status: 404 });
|
|
4570
|
+
}
|
|
4571
|
+
});
|
|
4572
|
+
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`;
|
|
4573
|
+
const msg = `
|
|
4574
|
+
${import_picocolors16.default.blue("\u25C9")} doraval local dashboard
|
|
4575
|
+
${import_picocolors16.default.dim("Project:")} ${project ? import_picocolors16.default.white(project) : import_picocolors16.default.yellow("none (run dora init)")}
|
|
4576
|
+
${import_picocolors16.default.dim("URL:")} ${import_picocolors16.default.underline(import_picocolors16.default.cyan(url))}
|
|
4577
|
+
|
|
4578
|
+
${import_picocolors16.default.dim("Press Ctrl+C to stop")}
|
|
4579
|
+
`;
|
|
4580
|
+
console.error(msg);
|
|
4581
|
+
if (shouldOpen && process.stdout.isTTY) {
|
|
4582
|
+
try {
|
|
4583
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
4584
|
+
spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
|
|
4585
|
+
} catch {
|
|
4586
|
+
console.error(import_picocolors16.default.dim(` Could not auto-open. Visit ${url}`));
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
process.on("SIGINT", () => {
|
|
4590
|
+
console.error(`
|
|
4591
|
+
Stopping dashboard...`);
|
|
4592
|
+
server.stop();
|
|
4593
|
+
process.exit(0);
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4596
|
+
};
|
|
4597
|
+
});
|
|
4598
|
+
|
|
3888
4599
|
// src/validators/claude/skill.ts
|
|
3889
|
-
import { existsSync as
|
|
4600
|
+
import { existsSync as existsSync20 } from "fs";
|
|
3890
4601
|
import { resolve as resolve5 } from "path";
|
|
3891
4602
|
var claudeSkillValidator;
|
|
3892
4603
|
var init_skill = __esm(() => {
|
|
@@ -3897,7 +4608,7 @@ var init_skill = __esm(() => {
|
|
|
3897
4608
|
name: "Claude Skill",
|
|
3898
4609
|
description: "Validates SKILL.md per current Claude Code spec: frontmatter (name/description relaxed to recommended; directory name usually provides the /command), body, supporting files, dynamic injection (!`cmd`), substitutions ($ARGUMENTS, ${CLAUDE_*}), and advanced fields (allowed-tools, context, disable-model-invocation, when_to_use, etc.)",
|
|
3899
4610
|
detect(dir) {
|
|
3900
|
-
return
|
|
4611
|
+
return existsSync20(resolve5(dir, "SKILL.md"));
|
|
3901
4612
|
},
|
|
3902
4613
|
async validate(dir, _opts) {
|
|
3903
4614
|
const loaded = await loadSkill(dir);
|
|
@@ -3915,8 +4626,8 @@ var init_skill = __esm(() => {
|
|
|
3915
4626
|
});
|
|
3916
4627
|
|
|
3917
4628
|
// src/validators/claude/plugin.ts
|
|
3918
|
-
import { existsSync as
|
|
3919
|
-
import { resolve as resolve6, join as
|
|
4629
|
+
import { existsSync as existsSync21, readdirSync as readdirSync10 } from "fs";
|
|
4630
|
+
import { resolve as resolve6, join as join19 } from "path";
|
|
3920
4631
|
function levenshtein(a, b) {
|
|
3921
4632
|
if (a === b)
|
|
3922
4633
|
return 0;
|
|
@@ -4008,7 +4719,7 @@ var init_plugin = __esm(() => {
|
|
|
4008
4719
|
name: "Claude Plugin",
|
|
4009
4720
|
description: "Validates .claude-plugin/plugin.json manifest (complete schema per Plugins reference), component path rules (replace vs augment), .claude-plugin/ purity, default dirs, single-root-skill layout, unrecognized fields + suggestions, and structure",
|
|
4010
4721
|
detect(dir) {
|
|
4011
|
-
return
|
|
4722
|
+
return existsSync21(resolve6(dir, ".claude-plugin", "plugin.json"));
|
|
4012
4723
|
},
|
|
4013
4724
|
async validate(dir, _opts) {
|
|
4014
4725
|
const errors = [];
|
|
@@ -4021,12 +4732,17 @@ var init_plugin = __esm(() => {
|
|
|
4021
4732
|
const raw = await Bun.file(manifestPath).text();
|
|
4022
4733
|
manifest = JSON.parse(raw);
|
|
4023
4734
|
passes.push(".claude-plugin/plugin.json is valid JSON");
|
|
4024
|
-
} catch {
|
|
4025
|
-
|
|
4735
|
+
} catch (err) {
|
|
4736
|
+
if (!existsSync21(manifestPath)) {
|
|
4737
|
+
errors.push(`.claude-plugin/plugin.json is missing (looked for ${manifestPath})`);
|
|
4738
|
+
warnings.push("Hint: Run `doraval claude new` (or `dora claude new`) to scaffold a new Claude plugin in this directory.");
|
|
4739
|
+
} else {
|
|
4740
|
+
errors.push(`.claude-plugin/plugin.json is invalid JSON (${err.message})`);
|
|
4741
|
+
}
|
|
4026
4742
|
return { errors, warnings, passes };
|
|
4027
4743
|
}
|
|
4028
4744
|
try {
|
|
4029
|
-
const entries =
|
|
4745
|
+
const entries = readdirSync10(dotClaudePluginDir);
|
|
4030
4746
|
const unexpected = entries.filter((e) => e !== "plugin.json");
|
|
4031
4747
|
if (unexpected.length > 0) {
|
|
4032
4748
|
for (const e of unexpected) {
|
|
@@ -4082,10 +4798,12 @@ var init_plugin = __esm(() => {
|
|
|
4082
4798
|
}
|
|
4083
4799
|
if (manifest.keywords !== undefined) {
|
|
4084
4800
|
if (Array.isArray(manifest.keywords)) {
|
|
4085
|
-
passes.push(`keywords: [${manifest.keywords.join(", ")}]`);
|
|
4801
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Claude Code`);
|
|
4086
4802
|
} else {
|
|
4087
4803
|
errors.push("keywords must be an array of strings");
|
|
4088
4804
|
}
|
|
4805
|
+
} else {
|
|
4806
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Claude Code)');
|
|
4089
4807
|
}
|
|
4090
4808
|
if (manifest.defaultEnabled !== undefined) {
|
|
4091
4809
|
passes.push(`defaultEnabled: ${manifest.defaultEnabled}`);
|
|
@@ -4111,7 +4829,7 @@ var init_plugin = __esm(() => {
|
|
|
4111
4829
|
errors.push(`${field}: path "${s}" must start with "./"`);
|
|
4112
4830
|
} else if (s.includes("..")) {
|
|
4113
4831
|
errors.push(`${field}: path "${s}" must not use ".." (paths are confined to the plugin tree after cache copy)`);
|
|
4114
|
-
} else if (
|
|
4832
|
+
} else if (existsSync21(resolve6(dir, s))) {
|
|
4115
4833
|
passes.push(`${field}: path "${s}" exists`);
|
|
4116
4834
|
} else {
|
|
4117
4835
|
warnings.push(`${field}: path "${s}" does not exist on disk`);
|
|
@@ -4161,11 +4879,11 @@ var init_plugin = __esm(() => {
|
|
|
4161
4879
|
passes.push(`dependencies: declares ${manifest.dependencies.length} plugin dependency/ies`);
|
|
4162
4880
|
}
|
|
4163
4881
|
const skillsDir = resolve6(dir, "skills");
|
|
4164
|
-
if (
|
|
4165
|
-
const entries =
|
|
4882
|
+
if (existsSync21(skillsDir)) {
|
|
4883
|
+
const entries = readdirSync10(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
4166
4884
|
for (const e of entries) {
|
|
4167
|
-
const md =
|
|
4168
|
-
if (
|
|
4885
|
+
const md = join19(skillsDir, e.name, "SKILL.md");
|
|
4886
|
+
if (existsSync21(md)) {
|
|
4169
4887
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
4170
4888
|
} else {
|
|
4171
4889
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -4176,8 +4894,8 @@ var init_plugin = __esm(() => {
|
|
|
4176
4894
|
}
|
|
4177
4895
|
}
|
|
4178
4896
|
const commandsDir = resolve6(dir, "commands");
|
|
4179
|
-
if (
|
|
4180
|
-
const mds =
|
|
4897
|
+
if (existsSync21(commandsDir)) {
|
|
4898
|
+
const mds = readdirSync10(commandsDir).filter((f) => f.endsWith(".md"));
|
|
4181
4899
|
if (mds.length) {
|
|
4182
4900
|
passes.push(`commands/ has ${mds.length} .md file(s)`);
|
|
4183
4901
|
}
|
|
@@ -4186,8 +4904,8 @@ var init_plugin = __esm(() => {
|
|
|
4186
4904
|
}
|
|
4187
4905
|
}
|
|
4188
4906
|
const agentsDir = resolve6(dir, "agents");
|
|
4189
|
-
if (
|
|
4190
|
-
const mds =
|
|
4907
|
+
if (existsSync21(agentsDir)) {
|
|
4908
|
+
const mds = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
|
|
4191
4909
|
if (mds.length) {
|
|
4192
4910
|
passes.push(`agents/ has ${mds.length} .md file(s)`);
|
|
4193
4911
|
}
|
|
@@ -4195,30 +4913,30 @@ var init_plugin = __esm(() => {
|
|
|
4195
4913
|
warnings.push('agents/ co-exists with manifest "agents" \u2014 manifest replaces default (dir ignored)');
|
|
4196
4914
|
}
|
|
4197
4915
|
}
|
|
4198
|
-
if (
|
|
4916
|
+
if (existsSync21(resolve6(dir, "output-styles"))) {
|
|
4199
4917
|
passes.push("output-styles/ directory present");
|
|
4200
4918
|
if (manifest.outputStyles)
|
|
4201
4919
|
warnings.push("output-styles/ co-exists with manifest outputStyles \u2014 manifest wins");
|
|
4202
4920
|
}
|
|
4203
|
-
if (
|
|
4921
|
+
if (existsSync21(resolve6(dir, "themes")))
|
|
4204
4922
|
passes.push("themes/ present (experimental)");
|
|
4205
|
-
if (
|
|
4923
|
+
if (existsSync21(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
|
|
4206
4924
|
passes.push("monitors config present (experimental)");
|
|
4207
4925
|
}
|
|
4208
|
-
if (
|
|
4926
|
+
if (existsSync21(resolve6(dir, "bin")))
|
|
4209
4927
|
passes.push("bin/ present (adds executables to Bash tool $PATH)");
|
|
4210
|
-
if (
|
|
4928
|
+
if (existsSync21(resolve6(dir, "settings.json")))
|
|
4211
4929
|
passes.push("settings.json present (plugin defaults for agent/statusline)");
|
|
4212
|
-
if (
|
|
4930
|
+
if (existsSync21(resolve6(dir, "README.md")))
|
|
4213
4931
|
passes.push("README.md present");
|
|
4214
|
-
if (
|
|
4932
|
+
if (existsSync21(resolve6(dir, ".mcp.json")))
|
|
4215
4933
|
passes.push(".mcp.json present (validated by claude:mcp)");
|
|
4216
|
-
if (
|
|
4934
|
+
if (existsSync21(resolve6(dir, ".lsp.json")))
|
|
4217
4935
|
passes.push(".lsp.json present (validated by claude:lsp when registered)");
|
|
4218
|
-
if (
|
|
4936
|
+
if (existsSync21(resolve6(dir, "hooks/hooks.json")) || existsSync21(resolve6(dir, "hooks.json"))) {
|
|
4219
4937
|
passes.push("hooks config present (validated by claude:hooks)");
|
|
4220
4938
|
}
|
|
4221
|
-
if (
|
|
4939
|
+
if (existsSync21(resolve6(dir, "SKILL.md")) && !existsSync21(skillsDir) && manifest.skills === undefined) {
|
|
4222
4940
|
passes.push('Root SKILL.md detected \u2014 plugin will be treated as a single-skill plugin (prefer frontmatter "name" for stable /command)');
|
|
4223
4941
|
}
|
|
4224
4942
|
return { errors, warnings, passes };
|
|
@@ -4227,8 +4945,8 @@ var init_plugin = __esm(() => {
|
|
|
4227
4945
|
});
|
|
4228
4946
|
|
|
4229
4947
|
// src/validators/claude/marketplace.ts
|
|
4230
|
-
import { existsSync as
|
|
4231
|
-
import { resolve as resolve7, join as
|
|
4948
|
+
import { existsSync as existsSync22, readdirSync as readdirSync11 } from "fs";
|
|
4949
|
+
import { resolve as resolve7, join as join20 } from "path";
|
|
4232
4950
|
var claudeMarketplaceValidator;
|
|
4233
4951
|
var init_marketplace = __esm(() => {
|
|
4234
4952
|
claudeMarketplaceValidator = {
|
|
@@ -4237,18 +4955,18 @@ var init_marketplace = __esm(() => {
|
|
|
4237
4955
|
name: "Claude Plugin Marketplace",
|
|
4238
4956
|
description: "Validates .claude-plugin/marketplace.json or plugins/ marketplace layouts (plugins array with sources)",
|
|
4239
4957
|
detect(dir) {
|
|
4240
|
-
if (
|
|
4958
|
+
if (existsSync22(resolve7(dir, ".claude-plugin", "marketplace.json")))
|
|
4241
4959
|
return true;
|
|
4242
4960
|
const pluginsDir = resolve7(dir, "plugins");
|
|
4243
|
-
if (!
|
|
4961
|
+
if (!existsSync22(pluginsDir))
|
|
4244
4962
|
return false;
|
|
4245
4963
|
try {
|
|
4246
|
-
const entries =
|
|
4964
|
+
const entries = readdirSync11(pluginsDir, { withFileTypes: true });
|
|
4247
4965
|
for (const entry of entries) {
|
|
4248
4966
|
if (!entry.isDirectory())
|
|
4249
4967
|
continue;
|
|
4250
|
-
const hasSkills =
|
|
4251
|
-
const hasManifest =
|
|
4968
|
+
const hasSkills = existsSync22(join20(pluginsDir, entry.name, "skills"));
|
|
4969
|
+
const hasManifest = existsSync22(join20(pluginsDir, entry.name, ".claude-plugin", "plugin.json"));
|
|
4252
4970
|
if (hasSkills || hasManifest)
|
|
4253
4971
|
return true;
|
|
4254
4972
|
}
|
|
@@ -4260,9 +4978,9 @@ var init_marketplace = __esm(() => {
|
|
|
4260
4978
|
const warnings = [];
|
|
4261
4979
|
const passes = [];
|
|
4262
4980
|
const claudeMktPath = resolve7(dir, ".claude-plugin", "marketplace.json");
|
|
4263
|
-
const hasClaudeMkt =
|
|
4981
|
+
const hasClaudeMkt = existsSync22(claudeMktPath);
|
|
4264
4982
|
const pluginsDir = resolve7(dir, "plugins");
|
|
4265
|
-
const hasPluginsDirLayout =
|
|
4983
|
+
const hasPluginsDirLayout = existsSync22(pluginsDir);
|
|
4266
4984
|
if (!hasClaudeMkt && !hasPluginsDirLayout) {
|
|
4267
4985
|
errors.push("Missing .claude-plugin/marketplace.json or plugins/ directory");
|
|
4268
4986
|
return { errors, warnings, passes };
|
|
@@ -4307,9 +5025,9 @@ var init_marketplace = __esm(() => {
|
|
|
4307
5025
|
const src = String(p.source);
|
|
4308
5026
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
4309
5027
|
const srcDir = resolve7(dir, src);
|
|
4310
|
-
if (
|
|
4311
|
-
const hasManifest =
|
|
4312
|
-
const hasSkills =
|
|
5028
|
+
if (existsSync22(srcDir)) {
|
|
5029
|
+
const hasManifest = existsSync22(resolve7(srcDir, ".claude-plugin", "plugin.json"));
|
|
5030
|
+
const hasSkills = existsSync22(resolve7(srcDir, "skills"));
|
|
4313
5031
|
if (hasManifest || hasSkills) {
|
|
4314
5032
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
4315
5033
|
} else {
|
|
@@ -4325,12 +5043,12 @@ var init_marketplace = __esm(() => {
|
|
|
4325
5043
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
4326
5044
|
}
|
|
4327
5045
|
}
|
|
4328
|
-
if (
|
|
5046
|
+
if (existsSync22(resolve7(dir, "README.md"))) {
|
|
4329
5047
|
passes.push("README.md exists at marketplace root");
|
|
4330
5048
|
} else {
|
|
4331
5049
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
4332
5050
|
}
|
|
4333
|
-
if (
|
|
5051
|
+
if (existsSync22(resolve7(dir, "LICENSE"))) {
|
|
4334
5052
|
passes.push("LICENSE exists at marketplace root");
|
|
4335
5053
|
} else {
|
|
4336
5054
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -4339,27 +5057,27 @@ var init_marketplace = __esm(() => {
|
|
|
4339
5057
|
}
|
|
4340
5058
|
if (hasPluginsDirLayout) {
|
|
4341
5059
|
passes.push("plugins/ directory exists");
|
|
4342
|
-
const pluginEntries =
|
|
5060
|
+
const pluginEntries = readdirSync11(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
4343
5061
|
if (pluginEntries.length === 0) {
|
|
4344
5062
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
4345
5063
|
return { errors, warnings, passes };
|
|
4346
5064
|
}
|
|
4347
5065
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
4348
|
-
if (
|
|
5066
|
+
if (existsSync22(resolve7(dir, "README.md"))) {
|
|
4349
5067
|
passes.push("README.md exists at marketplace root");
|
|
4350
5068
|
} else {
|
|
4351
5069
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
4352
5070
|
}
|
|
4353
|
-
if (
|
|
5071
|
+
if (existsSync22(resolve7(dir, "LICENSE"))) {
|
|
4354
5072
|
passes.push("LICENSE exists at marketplace root");
|
|
4355
5073
|
} else {
|
|
4356
5074
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
4357
5075
|
}
|
|
4358
5076
|
for (const plugin of pluginEntries) {
|
|
4359
|
-
const pluginPath =
|
|
4360
|
-
const hasSkills =
|
|
4361
|
-
const hasManifest =
|
|
4362
|
-
const hasReadme =
|
|
5077
|
+
const pluginPath = join20(pluginsDir, plugin.name);
|
|
5078
|
+
const hasSkills = existsSync22(join20(pluginPath, "skills"));
|
|
5079
|
+
const hasManifest = existsSync22(join20(pluginPath, ".claude-plugin", "plugin.json"));
|
|
5080
|
+
const hasReadme = existsSync22(join20(pluginPath, "README.md"));
|
|
4363
5081
|
if (hasManifest || hasSkills) {
|
|
4364
5082
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
4365
5083
|
} else {
|
|
@@ -4377,7 +5095,7 @@ var init_marketplace = __esm(() => {
|
|
|
4377
5095
|
});
|
|
4378
5096
|
|
|
4379
5097
|
// src/validators/claude/hooks.ts
|
|
4380
|
-
import { existsSync as
|
|
5098
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4381
5099
|
import { resolve as resolve8 } from "path";
|
|
4382
5100
|
var KNOWN_EVENTS, claudeHooksValidator;
|
|
4383
5101
|
var init_hooks = __esm(() => {
|
|
@@ -4419,13 +5137,13 @@ var init_hooks = __esm(() => {
|
|
|
4419
5137
|
name: "Claude Hooks",
|
|
4420
5138
|
description: "Validates hooks/hooks.json (or root hooks.json): all lifecycle events per Plugins reference, hook group structure (matcher + hooks[]), supported hook types (command, http, mcp_tool, prompt, agent)",
|
|
4421
5139
|
detect(dir) {
|
|
4422
|
-
return
|
|
5140
|
+
return existsSync23(resolve8(dir, "hooks", "hooks.json")) || existsSync23(resolve8(dir, "hooks.json"));
|
|
4423
5141
|
},
|
|
4424
5142
|
async validate(dir, _opts) {
|
|
4425
5143
|
const errors = [];
|
|
4426
5144
|
const warnings = [];
|
|
4427
5145
|
const passes = [];
|
|
4428
|
-
const hooksPath =
|
|
5146
|
+
const hooksPath = existsSync23(resolve8(dir, "hooks", "hooks.json")) ? resolve8(dir, "hooks", "hooks.json") : resolve8(dir, "hooks.json");
|
|
4429
5147
|
let config;
|
|
4430
5148
|
try {
|
|
4431
5149
|
const raw = await Bun.file(hooksPath).text();
|
|
@@ -4491,7 +5209,7 @@ var init_hooks = __esm(() => {
|
|
|
4491
5209
|
});
|
|
4492
5210
|
|
|
4493
5211
|
// src/validators/claude/mcp.ts
|
|
4494
|
-
import { existsSync as
|
|
5212
|
+
import { existsSync as existsSync24 } from "fs";
|
|
4495
5213
|
import { resolve as resolve9 } from "path";
|
|
4496
5214
|
var claudeMcpValidator;
|
|
4497
5215
|
var init_mcp = __esm(() => {
|
|
@@ -4501,7 +5219,7 @@ var init_mcp = __esm(() => {
|
|
|
4501
5219
|
name: "Claude MCP Config",
|
|
4502
5220
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, ${CLAUDE_PLUGIN_ROOT} etc. substitutions per Plugins reference",
|
|
4503
5221
|
detect(dir) {
|
|
4504
|
-
return
|
|
5222
|
+
return existsSync24(resolve9(dir, ".mcp.json"));
|
|
4505
5223
|
},
|
|
4506
5224
|
async validate(dir, _opts) {
|
|
4507
5225
|
const errors = [];
|
|
@@ -4561,8 +5279,8 @@ var init_mcp = __esm(() => {
|
|
|
4561
5279
|
});
|
|
4562
5280
|
|
|
4563
5281
|
// src/validators/claude/subagent.ts
|
|
4564
|
-
import { existsSync as
|
|
4565
|
-
import { resolve as resolve10, join as
|
|
5282
|
+
import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
|
|
5283
|
+
import { resolve as resolve10, join as join21 } from "path";
|
|
4566
5284
|
var claudeSubagentValidator;
|
|
4567
5285
|
var init_subagent = __esm(() => {
|
|
4568
5286
|
init_frontmatter();
|
|
@@ -4573,10 +5291,10 @@ var init_subagent = __esm(() => {
|
|
|
4573
5291
|
description: "Validates agents/*.md (plugin subagents): frontmatter per spec (name, description, model, effort, maxTurns, tools, disallowedTools, skills, memory, background, isolation=worktree), body; warns on disallowed fields (hooks, mcpServers, permissionMode) for security",
|
|
4574
5292
|
detect(dir) {
|
|
4575
5293
|
const agentsDir = resolve10(dir, "agents");
|
|
4576
|
-
if (!
|
|
5294
|
+
if (!existsSync25(agentsDir))
|
|
4577
5295
|
return false;
|
|
4578
5296
|
try {
|
|
4579
|
-
return
|
|
5297
|
+
return readdirSync12(agentsDir).some((f) => f.endsWith(".md"));
|
|
4580
5298
|
} catch {
|
|
4581
5299
|
return false;
|
|
4582
5300
|
}
|
|
@@ -4586,7 +5304,7 @@ var init_subagent = __esm(() => {
|
|
|
4586
5304
|
const warnings = [];
|
|
4587
5305
|
const passes = [];
|
|
4588
5306
|
const agentsDir = resolve10(dir, "agents");
|
|
4589
|
-
const mdFiles =
|
|
5307
|
+
const mdFiles = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
|
|
4590
5308
|
if (mdFiles.length === 0) {
|
|
4591
5309
|
errors.push("agents/ directory has no .md files");
|
|
4592
5310
|
return { errors, warnings, passes };
|
|
@@ -4607,7 +5325,7 @@ var init_subagent = __esm(() => {
|
|
|
4607
5325
|
]);
|
|
4608
5326
|
const DISALLOWED = new Set(["hooks", "mcpServers", "permissionMode"]);
|
|
4609
5327
|
for (const file of mdFiles) {
|
|
4610
|
-
const filePath =
|
|
5328
|
+
const filePath = join21(agentsDir, file);
|
|
4611
5329
|
const raw = await Bun.file(filePath).text();
|
|
4612
5330
|
try {
|
|
4613
5331
|
const parsed = parseFrontmatter(raw);
|
|
@@ -4653,8 +5371,8 @@ var init_subagent = __esm(() => {
|
|
|
4653
5371
|
});
|
|
4654
5372
|
|
|
4655
5373
|
// src/validators/claude/command.ts
|
|
4656
|
-
import { existsSync as
|
|
4657
|
-
import { resolve as resolve11, join as
|
|
5374
|
+
import { existsSync as existsSync26, readdirSync as readdirSync13 } from "fs";
|
|
5375
|
+
import { resolve as resolve11, join as join22 } from "path";
|
|
4658
5376
|
var claudeCommandValidator;
|
|
4659
5377
|
var init_command = __esm(() => {
|
|
4660
5378
|
init_frontmatter();
|
|
@@ -4665,10 +5383,10 @@ var init_command = __esm(() => {
|
|
|
4665
5383
|
description: "Validates commands/ (or legacy .claude/commands/) .md files: frontmatter (including rich skill fields), description, body",
|
|
4666
5384
|
detect(dir) {
|
|
4667
5385
|
const commandsDir = resolve11(dir, "commands");
|
|
4668
|
-
if (!
|
|
5386
|
+
if (!existsSync26(commandsDir))
|
|
4669
5387
|
return false;
|
|
4670
5388
|
try {
|
|
4671
|
-
return
|
|
5389
|
+
return readdirSync13(commandsDir).some((f) => f.endsWith(".md"));
|
|
4672
5390
|
} catch {
|
|
4673
5391
|
return false;
|
|
4674
5392
|
}
|
|
@@ -4678,14 +5396,14 @@ var init_command = __esm(() => {
|
|
|
4678
5396
|
const warnings = [];
|
|
4679
5397
|
const passes = [];
|
|
4680
5398
|
const commandsDir = resolve11(dir, "commands");
|
|
4681
|
-
const mdFiles =
|
|
5399
|
+
const mdFiles = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
|
|
4682
5400
|
if (mdFiles.length === 0) {
|
|
4683
5401
|
errors.push("commands/ directory has no .md files");
|
|
4684
5402
|
return { errors, warnings, passes };
|
|
4685
5403
|
}
|
|
4686
5404
|
passes.push(`${mdFiles.length} command definition(s) found`);
|
|
4687
5405
|
for (const file of mdFiles) {
|
|
4688
|
-
const filePath =
|
|
5406
|
+
const filePath = join22(commandsDir, file);
|
|
4689
5407
|
const raw = await Bun.file(filePath).text();
|
|
4690
5408
|
try {
|
|
4691
5409
|
const parsed = parseFrontmatter(raw);
|
|
@@ -4714,7 +5432,7 @@ var init_command = __esm(() => {
|
|
|
4714
5432
|
});
|
|
4715
5433
|
|
|
4716
5434
|
// src/validators/claude/memory.ts
|
|
4717
|
-
import { existsSync as
|
|
5435
|
+
import { existsSync as existsSync27 } from "fs";
|
|
4718
5436
|
import { resolve as resolve12 } from "path";
|
|
4719
5437
|
var claudeMemoryValidator;
|
|
4720
5438
|
var init_memory = __esm(() => {
|
|
@@ -4724,7 +5442,7 @@ var init_memory = __esm(() => {
|
|
|
4724
5442
|
name: "Claude CLAUDE.md",
|
|
4725
5443
|
description: "Validates CLAUDE.md: non-empty, length recommendations, @path imports",
|
|
4726
5444
|
detect(dir) {
|
|
4727
|
-
return
|
|
5445
|
+
return existsSync27(resolve12(dir, "CLAUDE.md"));
|
|
4728
5446
|
},
|
|
4729
5447
|
async validate(dir, _opts) {
|
|
4730
5448
|
const errors = [];
|
|
@@ -4749,7 +5467,7 @@ var init_memory = __esm(() => {
|
|
|
4749
5467
|
while ((match = importRegex.exec(raw)) !== null) {
|
|
4750
5468
|
const importPath = match[1];
|
|
4751
5469
|
const resolvedImport = resolve12(dir, importPath);
|
|
4752
|
-
if (
|
|
5470
|
+
if (existsSync27(resolvedImport)) {
|
|
4753
5471
|
passes.push(`@import "${importPath}" exists`);
|
|
4754
5472
|
} else {
|
|
4755
5473
|
warnings.push(`@import "${importPath}" \u2014 file not found at ${resolvedImport}`);
|
|
@@ -4761,7 +5479,7 @@ var init_memory = __esm(() => {
|
|
|
4761
5479
|
});
|
|
4762
5480
|
|
|
4763
5481
|
// src/validators/claude/lsp.ts
|
|
4764
|
-
import { existsSync as
|
|
5482
|
+
import { existsSync as existsSync28 } from "fs";
|
|
4765
5483
|
import { resolve as resolve13 } from "path";
|
|
4766
5484
|
var claudeLspValidator;
|
|
4767
5485
|
var init_lsp = __esm(() => {
|
|
@@ -4771,7 +5489,7 @@ var init_lsp = __esm(() => {
|
|
|
4771
5489
|
name: "Claude LSP Servers",
|
|
4772
5490
|
description: "Validates .lsp.json (or plugin.json lspServers): language server configs with required command + extensionToLanguage; optional transport, env, settings, diagnostics etc. (binaries installed separately)",
|
|
4773
5491
|
detect(dir) {
|
|
4774
|
-
return
|
|
5492
|
+
return existsSync28(resolve13(dir, ".lsp.json")) || existsSync28(resolve13(dir, ".claude-plugin", "plugin.json"));
|
|
4775
5493
|
},
|
|
4776
5494
|
async validate(dir, _opts) {
|
|
4777
5495
|
const errors = [];
|
|
@@ -4779,7 +5497,7 @@ var init_lsp = __esm(() => {
|
|
|
4779
5497
|
const passes = [];
|
|
4780
5498
|
let cfg = null;
|
|
4781
5499
|
const lspPath = resolve13(dir, ".lsp.json");
|
|
4782
|
-
if (
|
|
5500
|
+
if (existsSync28(lspPath)) {
|
|
4783
5501
|
try {
|
|
4784
5502
|
cfg = JSON.parse(await Bun.file(lspPath).text());
|
|
4785
5503
|
passes.push(".lsp.json is valid JSON");
|
|
@@ -4789,7 +5507,7 @@ var init_lsp = __esm(() => {
|
|
|
4789
5507
|
}
|
|
4790
5508
|
} else {
|
|
4791
5509
|
const manifestPath = resolve13(dir, ".claude-plugin", "plugin.json");
|
|
4792
|
-
if (
|
|
5510
|
+
if (existsSync28(manifestPath)) {
|
|
4793
5511
|
try {
|
|
4794
5512
|
const m = JSON.parse(await Bun.file(manifestPath).text());
|
|
4795
5513
|
if (m && m.lspServers && typeof m.lspServers === "object") {
|
|
@@ -4800,7 +5518,7 @@ var init_lsp = __esm(() => {
|
|
|
4800
5518
|
}
|
|
4801
5519
|
}
|
|
4802
5520
|
if (!cfg) {
|
|
4803
|
-
if (!
|
|
5521
|
+
if (!existsSync28(lspPath)) {
|
|
4804
5522
|
return { errors, warnings, passes };
|
|
4805
5523
|
}
|
|
4806
5524
|
}
|
|
@@ -4829,7 +5547,7 @@ var init_lsp = __esm(() => {
|
|
|
4829
5547
|
});
|
|
4830
5548
|
|
|
4831
5549
|
// src/validators/claude/monitors.ts
|
|
4832
|
-
import { existsSync as
|
|
5550
|
+
import { existsSync as existsSync29 } from "fs";
|
|
4833
5551
|
import { resolve as resolve14 } from "path";
|
|
4834
5552
|
var claudeMonitorsValidator;
|
|
4835
5553
|
var init_monitors = __esm(() => {
|
|
@@ -4839,7 +5557,7 @@ var init_monitors = __esm(() => {
|
|
|
4839
5557
|
name: "Claude Monitors (experimental)",
|
|
4840
5558
|
description: "Validates monitors/monitors.json (or experimental.monitors): array of {name, command, description, when?}; commands support ${CLAUDE_PLUGIN_*} subs. Monitors run only in interactive CLI sessions.",
|
|
4841
5559
|
detect(dir) {
|
|
4842
|
-
return
|
|
5560
|
+
return existsSync29(resolve14(dir, "monitors", "monitors.json")) || existsSync29(resolve14(dir, "monitors.json")) || existsSync29(resolve14(dir, ".claude-plugin", "plugin.json"));
|
|
4843
5561
|
},
|
|
4844
5562
|
async validate(dir, _opts) {
|
|
4845
5563
|
const errors = [];
|
|
@@ -4851,7 +5569,7 @@ var init_monitors = __esm(() => {
|
|
|
4851
5569
|
resolve14(dir, "monitors.json")
|
|
4852
5570
|
];
|
|
4853
5571
|
for (const p of candidates) {
|
|
4854
|
-
if (
|
|
5572
|
+
if (existsSync29(p)) {
|
|
4855
5573
|
try {
|
|
4856
5574
|
const parsed = JSON.parse(await Bun.file(p).text());
|
|
4857
5575
|
if (Array.isArray(parsed)) {
|
|
@@ -4867,7 +5585,7 @@ var init_monitors = __esm(() => {
|
|
|
4867
5585
|
}
|
|
4868
5586
|
if (!arr) {
|
|
4869
5587
|
const mp = resolve14(dir, ".claude-plugin", "plugin.json");
|
|
4870
|
-
if (
|
|
5588
|
+
if (existsSync29(mp)) {
|
|
4871
5589
|
try {
|
|
4872
5590
|
const m = JSON.parse(await Bun.file(mp).text());
|
|
4873
5591
|
const exp = m?.experimental;
|
|
@@ -4920,8 +5638,8 @@ var init_monitors = __esm(() => {
|
|
|
4920
5638
|
});
|
|
4921
5639
|
|
|
4922
5640
|
// src/validators/codex/plugin.ts
|
|
4923
|
-
import { existsSync as
|
|
4924
|
-
import { resolve as resolve15, join as
|
|
5641
|
+
import { existsSync as existsSync30, readdirSync as readdirSync14 } from "fs";
|
|
5642
|
+
import { resolve as resolve15, join as join23 } from "path";
|
|
4925
5643
|
var NAME_REGEX3, codexPluginValidator;
|
|
4926
5644
|
var init_plugin2 = __esm(() => {
|
|
4927
5645
|
NAME_REGEX3 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -4931,7 +5649,7 @@ var init_plugin2 = __esm(() => {
|
|
|
4931
5649
|
name: "Codex Plugin",
|
|
4932
5650
|
description: "Validates .codex-plugin/plugin.json manifest (requires interface block and skills as directory string per Codex packaging)",
|
|
4933
5651
|
detect(dir) {
|
|
4934
|
-
return
|
|
5652
|
+
return existsSync30(resolve15(dir, ".codex-plugin", "plugin.json"));
|
|
4935
5653
|
},
|
|
4936
5654
|
async validate(dir, _opts) {
|
|
4937
5655
|
const errors = [];
|
|
@@ -5002,12 +5720,21 @@ var init_plugin2 = __esm(() => {
|
|
|
5002
5720
|
} else {
|
|
5003
5721
|
warnings.push('Missing "description" (recommended)');
|
|
5004
5722
|
}
|
|
5723
|
+
if (manifest.keywords !== undefined) {
|
|
5724
|
+
if (Array.isArray(manifest.keywords)) {
|
|
5725
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Codex`);
|
|
5726
|
+
} else {
|
|
5727
|
+
errors.push("keywords must be an array of strings");
|
|
5728
|
+
}
|
|
5729
|
+
} else {
|
|
5730
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Codex)');
|
|
5731
|
+
}
|
|
5005
5732
|
const skillsDir = resolve15(dir, "skills");
|
|
5006
|
-
if (
|
|
5007
|
-
const entries =
|
|
5733
|
+
if (existsSync30(skillsDir)) {
|
|
5734
|
+
const entries = readdirSync14(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5008
5735
|
for (const e of entries) {
|
|
5009
|
-
const md =
|
|
5010
|
-
if (
|
|
5736
|
+
const md = join23(skillsDir, e.name, "SKILL.md");
|
|
5737
|
+
if (existsSync30(md)) {
|
|
5011
5738
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
5012
5739
|
} else {
|
|
5013
5740
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -5025,7 +5752,7 @@ var init_plugin2 = __esm(() => {
|
|
|
5025
5752
|
});
|
|
5026
5753
|
|
|
5027
5754
|
// src/validators/codex/marketplace.ts
|
|
5028
|
-
import { existsSync as
|
|
5755
|
+
import { existsSync as existsSync31 } from "fs";
|
|
5029
5756
|
import { resolve as resolve16 } from "path";
|
|
5030
5757
|
var codexMarketplaceValidator;
|
|
5031
5758
|
var init_marketplace2 = __esm(() => {
|
|
@@ -5035,9 +5762,9 @@ var init_marketplace2 = __esm(() => {
|
|
|
5035
5762
|
name: "Codex Plugin Marketplace",
|
|
5036
5763
|
description: "Validates .agents/plugins/marketplace.json (Codex convention: object source + policy blocks)",
|
|
5037
5764
|
detect(dir) {
|
|
5038
|
-
if (
|
|
5765
|
+
if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5039
5766
|
return true;
|
|
5040
|
-
if (
|
|
5767
|
+
if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5041
5768
|
return true;
|
|
5042
5769
|
return false;
|
|
5043
5770
|
},
|
|
@@ -5046,7 +5773,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5046
5773
|
const warnings = [];
|
|
5047
5774
|
const passes = [];
|
|
5048
5775
|
const marketplacePath = resolve16(dir, ".agents", "plugins", "marketplace.json");
|
|
5049
|
-
if (!
|
|
5776
|
+
if (!existsSync31(marketplacePath)) {
|
|
5050
5777
|
errors.push("Missing .agents/plugins/marketplace.json");
|
|
5051
5778
|
return { errors, warnings, passes };
|
|
5052
5779
|
}
|
|
@@ -5121,12 +5848,12 @@ var init_marketplace2 = __esm(() => {
|
|
|
5121
5848
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
5122
5849
|
}
|
|
5123
5850
|
}
|
|
5124
|
-
if (
|
|
5851
|
+
if (existsSync31(resolve16(dir, "README.md"))) {
|
|
5125
5852
|
passes.push("README.md exists at marketplace root");
|
|
5126
5853
|
} else {
|
|
5127
5854
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5128
5855
|
}
|
|
5129
|
-
if (
|
|
5856
|
+
if (existsSync31(resolve16(dir, "LICENSE"))) {
|
|
5130
5857
|
passes.push("LICENSE exists at marketplace root");
|
|
5131
5858
|
} else {
|
|
5132
5859
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5137,7 +5864,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5137
5864
|
});
|
|
5138
5865
|
|
|
5139
5866
|
// src/validators/codex/mcp.ts
|
|
5140
|
-
import { existsSync as
|
|
5867
|
+
import { existsSync as existsSync32 } from "fs";
|
|
5141
5868
|
import { resolve as resolve17 } from "path";
|
|
5142
5869
|
var codexMcpValidator;
|
|
5143
5870
|
var init_mcp2 = __esm(() => {
|
|
@@ -5147,7 +5874,7 @@ var init_mcp2 = __esm(() => {
|
|
|
5147
5874
|
name: "Codex MCP Config",
|
|
5148
5875
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, substitutions per Codex MCP support",
|
|
5149
5876
|
detect(dir) {
|
|
5150
|
-
return
|
|
5877
|
+
return existsSync32(resolve17(dir, ".mcp.json"));
|
|
5151
5878
|
},
|
|
5152
5879
|
async validate(dir, _opts) {
|
|
5153
5880
|
const errors = [];
|
|
@@ -5207,7 +5934,7 @@ var init_mcp2 = __esm(() => {
|
|
|
5207
5934
|
});
|
|
5208
5935
|
|
|
5209
5936
|
// src/validators/codex/skill.ts
|
|
5210
|
-
import { existsSync as
|
|
5937
|
+
import { existsSync as existsSync33 } from "fs";
|
|
5211
5938
|
import { resolve as resolve18 } from "path";
|
|
5212
5939
|
var codexSkillValidator;
|
|
5213
5940
|
var init_skill2 = __esm(() => {
|
|
@@ -5218,7 +5945,7 @@ var init_skill2 = __esm(() => {
|
|
|
5218
5945
|
name: "Codex Skill",
|
|
5219
5946
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Codex uses the same SKILL.md spec as other providers.",
|
|
5220
5947
|
detect(dir) {
|
|
5221
|
-
return
|
|
5948
|
+
return existsSync33(resolve18(dir, "SKILL.md"));
|
|
5222
5949
|
},
|
|
5223
5950
|
async validate(dir, _opts) {
|
|
5224
5951
|
const loaded = await loadSkill(dir);
|
|
@@ -5236,8 +5963,8 @@ var init_skill2 = __esm(() => {
|
|
|
5236
5963
|
});
|
|
5237
5964
|
|
|
5238
5965
|
// src/validators/cursor/plugin.ts
|
|
5239
|
-
import { existsSync as
|
|
5240
|
-
import { resolve as resolve19, join as
|
|
5966
|
+
import { existsSync as existsSync34, readdirSync as readdirSync16 } from "fs";
|
|
5967
|
+
import { resolve as resolve19, join as join25 } from "path";
|
|
5241
5968
|
var NAME_REGEX4, cursorPluginValidator;
|
|
5242
5969
|
var init_plugin3 = __esm(() => {
|
|
5243
5970
|
NAME_REGEX4 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -5247,7 +5974,7 @@ var init_plugin3 = __esm(() => {
|
|
|
5247
5974
|
name: "Cursor Plugin",
|
|
5248
5975
|
description: "Validates .cursor-plugin/plugin.json manifest (skills as directory string; mcpServers support)",
|
|
5249
5976
|
detect(dir) {
|
|
5250
|
-
return
|
|
5977
|
+
return existsSync34(resolve19(dir, ".cursor-plugin", "plugin.json"));
|
|
5251
5978
|
},
|
|
5252
5979
|
async validate(dir, _opts) {
|
|
5253
5980
|
const errors = [];
|
|
@@ -5324,15 +6051,21 @@ var init_plugin3 = __esm(() => {
|
|
|
5324
6051
|
passes.push("homepage present");
|
|
5325
6052
|
if (manifest.repository)
|
|
5326
6053
|
passes.push("repository present");
|
|
5327
|
-
if (
|
|
5328
|
-
|
|
6054
|
+
if (manifest.keywords !== undefined) {
|
|
6055
|
+
if (Array.isArray(manifest.keywords)) {
|
|
6056
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Cursor`);
|
|
6057
|
+
} else {
|
|
6058
|
+
errors.push("keywords must be an array of strings");
|
|
6059
|
+
}
|
|
6060
|
+
} else {
|
|
6061
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Cursor)');
|
|
5329
6062
|
}
|
|
5330
6063
|
const skillsDir = resolve19(dir, "skills");
|
|
5331
|
-
if (
|
|
5332
|
-
const entries =
|
|
6064
|
+
if (existsSync34(skillsDir)) {
|
|
6065
|
+
const entries = readdirSync16(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5333
6066
|
for (const e of entries) {
|
|
5334
|
-
const md =
|
|
5335
|
-
if (
|
|
6067
|
+
const md = join25(skillsDir, e.name, "SKILL.md");
|
|
6068
|
+
if (existsSync34(md)) {
|
|
5336
6069
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
5337
6070
|
} else {
|
|
5338
6071
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -5343,7 +6076,7 @@ var init_plugin3 = __esm(() => {
|
|
|
5343
6076
|
const mcpRef = manifest.mcpServers;
|
|
5344
6077
|
if (mcpRef.startsWith("./") || mcpRef.startsWith("../")) {
|
|
5345
6078
|
const mcpPath = resolve19(dir, mcpRef);
|
|
5346
|
-
if (
|
|
6079
|
+
if (existsSync34(mcpPath)) {
|
|
5347
6080
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
5348
6081
|
} else {
|
|
5349
6082
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -5373,8 +6106,8 @@ var init_plugin3 = __esm(() => {
|
|
|
5373
6106
|
});
|
|
5374
6107
|
|
|
5375
6108
|
// src/validators/cursor/marketplace.ts
|
|
5376
|
-
import { existsSync as
|
|
5377
|
-
import { resolve as resolve20, join as
|
|
6109
|
+
import { existsSync as existsSync35, readdirSync as readdirSync17 } from "fs";
|
|
6110
|
+
import { resolve as resolve20, join as join26 } from "path";
|
|
5378
6111
|
var cursorMarketplaceValidator;
|
|
5379
6112
|
var init_marketplace3 = __esm(() => {
|
|
5380
6113
|
cursorMarketplaceValidator = {
|
|
@@ -5383,18 +6116,18 @@ var init_marketplace3 = __esm(() => {
|
|
|
5383
6116
|
name: "Cursor Plugin Marketplace",
|
|
5384
6117
|
description: "Validates .cursor-plugin/marketplace.json (string sources + metadata.pluginRoot)",
|
|
5385
6118
|
detect(dir) {
|
|
5386
|
-
if (
|
|
6119
|
+
if (existsSync35(resolve20(dir, ".cursor-plugin", "marketplace.json")))
|
|
5387
6120
|
return true;
|
|
5388
6121
|
const pluginsDir = resolve20(dir, "plugins");
|
|
5389
|
-
if (!
|
|
6122
|
+
if (!existsSync35(pluginsDir))
|
|
5390
6123
|
return false;
|
|
5391
6124
|
try {
|
|
5392
|
-
const entries =
|
|
6125
|
+
const entries = readdirSync17(pluginsDir, { withFileTypes: true });
|
|
5393
6126
|
for (const entry of entries) {
|
|
5394
6127
|
if (!entry.isDirectory())
|
|
5395
6128
|
continue;
|
|
5396
|
-
const hasSkills =
|
|
5397
|
-
const hasManifest =
|
|
6129
|
+
const hasSkills = existsSync35(join26(pluginsDir, entry.name, "skills"));
|
|
6130
|
+
const hasManifest = existsSync35(join26(pluginsDir, entry.name, ".cursor-plugin", "plugin.json"));
|
|
5398
6131
|
if (hasSkills || hasManifest)
|
|
5399
6132
|
return true;
|
|
5400
6133
|
}
|
|
@@ -5406,9 +6139,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
5406
6139
|
const warnings = [];
|
|
5407
6140
|
const passes = [];
|
|
5408
6141
|
const cursorMktPath = resolve20(dir, ".cursor-plugin", "marketplace.json");
|
|
5409
|
-
const hasCursorMkt =
|
|
6142
|
+
const hasCursorMkt = existsSync35(cursorMktPath);
|
|
5410
6143
|
const pluginsDir = resolve20(dir, "plugins");
|
|
5411
|
-
const hasPluginsDirLayout =
|
|
6144
|
+
const hasPluginsDirLayout = existsSync35(pluginsDir);
|
|
5412
6145
|
if (!hasCursorMkt && !hasPluginsDirLayout) {
|
|
5413
6146
|
errors.push("Missing .cursor-plugin/marketplace.json or plugins/ directory");
|
|
5414
6147
|
return { errors, warnings, passes };
|
|
@@ -5462,9 +6195,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
5462
6195
|
const src = String(p.source);
|
|
5463
6196
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
5464
6197
|
const srcDir = resolve20(dir, pluginRoot, src);
|
|
5465
|
-
if (
|
|
5466
|
-
const hasManifest =
|
|
5467
|
-
const hasSkills =
|
|
6198
|
+
if (existsSync35(srcDir)) {
|
|
6199
|
+
const hasManifest = existsSync35(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
|
|
6200
|
+
const hasSkills = existsSync35(resolve20(srcDir, "skills"));
|
|
5468
6201
|
if (hasManifest || hasSkills) {
|
|
5469
6202
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
5470
6203
|
} else {
|
|
@@ -5475,7 +6208,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
5475
6208
|
}
|
|
5476
6209
|
} else {
|
|
5477
6210
|
const implicitSrc = resolve20(dir, pluginRoot, p.name || "");
|
|
5478
|
-
if (p.name &&
|
|
6211
|
+
if (p.name && existsSync35(implicitSrc)) {
|
|
5479
6212
|
passes.push(`plugins[${i}]: implicit source via name under ${pluginRoot}`);
|
|
5480
6213
|
} else {
|
|
5481
6214
|
warnings.push(`plugins[${i}]: missing "source" (and no implicit dir)`);
|
|
@@ -5490,12 +6223,12 @@ var init_marketplace3 = __esm(() => {
|
|
|
5490
6223
|
passes.push(`plugins[${i}].homepage present`);
|
|
5491
6224
|
}
|
|
5492
6225
|
}
|
|
5493
|
-
if (
|
|
6226
|
+
if (existsSync35(resolve20(dir, "README.md"))) {
|
|
5494
6227
|
passes.push("README.md exists at marketplace root");
|
|
5495
6228
|
} else {
|
|
5496
6229
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
5497
6230
|
}
|
|
5498
|
-
if (
|
|
6231
|
+
if (existsSync35(resolve20(dir, "LICENSE"))) {
|
|
5499
6232
|
passes.push("LICENSE exists at marketplace root");
|
|
5500
6233
|
} else {
|
|
5501
6234
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5504,21 +6237,21 @@ var init_marketplace3 = __esm(() => {
|
|
|
5504
6237
|
}
|
|
5505
6238
|
if (hasPluginsDirLayout) {
|
|
5506
6239
|
passes.push("plugins/ directory exists");
|
|
5507
|
-
const pluginEntries =
|
|
6240
|
+
const pluginEntries = readdirSync17(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5508
6241
|
if (pluginEntries.length === 0) {
|
|
5509
6242
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
5510
6243
|
return { errors, warnings, passes };
|
|
5511
6244
|
}
|
|
5512
6245
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
5513
|
-
if (
|
|
6246
|
+
if (existsSync35(resolve20(dir, "README.md"))) {
|
|
5514
6247
|
passes.push("README.md exists at marketplace root");
|
|
5515
6248
|
} else {
|
|
5516
6249
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5517
6250
|
}
|
|
5518
6251
|
for (const plugin of pluginEntries) {
|
|
5519
|
-
const pluginPath =
|
|
5520
|
-
const hasSkills =
|
|
5521
|
-
const hasManifest =
|
|
6252
|
+
const pluginPath = join26(pluginsDir, plugin.name);
|
|
6253
|
+
const hasSkills = existsSync35(join26(pluginPath, "skills"));
|
|
6254
|
+
const hasManifest = existsSync35(join26(pluginPath, ".cursor-plugin", "plugin.json"));
|
|
5522
6255
|
if (hasManifest || hasSkills) {
|
|
5523
6256
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
5524
6257
|
}
|
|
@@ -5531,7 +6264,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
5531
6264
|
});
|
|
5532
6265
|
|
|
5533
6266
|
// src/validators/cursor/mcp.ts
|
|
5534
|
-
import { existsSync as
|
|
6267
|
+
import { existsSync as existsSync36 } from "fs";
|
|
5535
6268
|
import { resolve as resolve21 } from "path";
|
|
5536
6269
|
var cursorMcpValidator;
|
|
5537
6270
|
var init_mcp3 = __esm(() => {
|
|
@@ -5541,7 +6274,7 @@ var init_mcp3 = __esm(() => {
|
|
|
5541
6274
|
name: "Cursor MCP Config",
|
|
5542
6275
|
description: "Validates mcp.json (Cursor uses no leading dot; supports mcpServers wrapper or direct server map)",
|
|
5543
6276
|
detect(dir) {
|
|
5544
|
-
return
|
|
6277
|
+
return existsSync36(resolve21(dir, "mcp.json"));
|
|
5545
6278
|
},
|
|
5546
6279
|
async validate(dir, _opts) {
|
|
5547
6280
|
const errors = [];
|
|
@@ -5607,7 +6340,7 @@ var init_mcp3 = __esm(() => {
|
|
|
5607
6340
|
});
|
|
5608
6341
|
|
|
5609
6342
|
// src/validators/cursor/skill.ts
|
|
5610
|
-
import { existsSync as
|
|
6343
|
+
import { existsSync as existsSync37 } from "fs";
|
|
5611
6344
|
import { resolve as resolve22 } from "path";
|
|
5612
6345
|
var cursorSkillValidator;
|
|
5613
6346
|
var init_skill3 = __esm(() => {
|
|
@@ -5618,7 +6351,7 @@ var init_skill3 = __esm(() => {
|
|
|
5618
6351
|
name: "Cursor Skill",
|
|
5619
6352
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Cursor uses the same SKILL.md spec as other providers.",
|
|
5620
6353
|
detect(dir) {
|
|
5621
|
-
return
|
|
6354
|
+
return existsSync37(resolve22(dir, "SKILL.md"));
|
|
5622
6355
|
},
|
|
5623
6356
|
async validate(dir, _opts) {
|
|
5624
6357
|
const loaded = await loadSkill(dir);
|
|
@@ -5636,7 +6369,7 @@ var init_skill3 = __esm(() => {
|
|
|
5636
6369
|
});
|
|
5637
6370
|
|
|
5638
6371
|
// src/validators/copilot/plugin.ts
|
|
5639
|
-
import { existsSync as
|
|
6372
|
+
import { existsSync as existsSync38 } from "fs";
|
|
5640
6373
|
import { resolve as resolve23 } from "path";
|
|
5641
6374
|
var NAME_REGEX5, copilotPluginValidator;
|
|
5642
6375
|
var init_plugin4 = __esm(() => {
|
|
@@ -5647,7 +6380,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5647
6380
|
name: "Copilot Plugin",
|
|
5648
6381
|
description: "Validates .github/plugin/plugin.json (skills as array of paths, mcpServers support)",
|
|
5649
6382
|
detect(dir) {
|
|
5650
|
-
return
|
|
6383
|
+
return existsSync38(resolve23(dir, ".github", "plugin", "plugin.json"));
|
|
5651
6384
|
},
|
|
5652
6385
|
async validate(dir, _opts) {
|
|
5653
6386
|
const errors = [];
|
|
@@ -5690,9 +6423,9 @@ var init_plugin4 = __esm(() => {
|
|
|
5690
6423
|
}
|
|
5691
6424
|
const skillDir = resolve23(dir, p);
|
|
5692
6425
|
const skillMd = resolve23(skillDir, "SKILL.md");
|
|
5693
|
-
if (
|
|
6426
|
+
if (existsSync38(skillMd)) {
|
|
5694
6427
|
passes.push(`skills[${i}]: ${p}/SKILL.md exists`);
|
|
5695
|
-
} else if (
|
|
6428
|
+
} else if (existsSync38(skillDir)) {
|
|
5696
6429
|
warnings.push(`skills[${i}]: directory exists but no SKILL.md inside`);
|
|
5697
6430
|
} else {
|
|
5698
6431
|
warnings.push(`skills[${i}]: path "${p}" does not exist`);
|
|
@@ -5704,7 +6437,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5704
6437
|
passes.push(`mcpServers: "${manifest.mcpServers}"`);
|
|
5705
6438
|
const mcpRef = String(manifest.mcpServers);
|
|
5706
6439
|
const mcpPath = resolve23(dir, mcpRef);
|
|
5707
|
-
if (
|
|
6440
|
+
if (existsSync38(mcpPath)) {
|
|
5708
6441
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
5709
6442
|
} else {
|
|
5710
6443
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -5733,8 +6466,14 @@ var init_plugin4 = __esm(() => {
|
|
|
5733
6466
|
passes.push("homepage present");
|
|
5734
6467
|
if (manifest.repository)
|
|
5735
6468
|
passes.push("repository present");
|
|
5736
|
-
if (
|
|
5737
|
-
|
|
6469
|
+
if (manifest.keywords !== undefined) {
|
|
6470
|
+
if (Array.isArray(manifest.keywords)) {
|
|
6471
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Copilot CLI`);
|
|
6472
|
+
} else {
|
|
6473
|
+
errors.push("keywords must be an array of strings");
|
|
6474
|
+
}
|
|
6475
|
+
} else {
|
|
6476
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Copilot CLI)');
|
|
5738
6477
|
}
|
|
5739
6478
|
const known = new Set([
|
|
5740
6479
|
"name",
|
|
@@ -5758,7 +6497,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5758
6497
|
});
|
|
5759
6498
|
|
|
5760
6499
|
// src/validators/copilot/marketplace.ts
|
|
5761
|
-
import { existsSync as
|
|
6500
|
+
import { existsSync as existsSync39 } from "fs";
|
|
5762
6501
|
import { resolve as resolve24 } from "path";
|
|
5763
6502
|
var copilotMarketplaceValidator;
|
|
5764
6503
|
var init_marketplace4 = __esm(() => {
|
|
@@ -5768,7 +6507,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
5768
6507
|
name: "Copilot Plugin Marketplace",
|
|
5769
6508
|
description: "Validates .github/plugin/marketplace.json (string sources)",
|
|
5770
6509
|
detect(dir) {
|
|
5771
|
-
return
|
|
6510
|
+
return existsSync39(resolve24(dir, ".github", "plugin", "marketplace.json"));
|
|
5772
6511
|
},
|
|
5773
6512
|
async validate(dir, _opts) {
|
|
5774
6513
|
const errors = [];
|
|
@@ -5818,9 +6557,9 @@ var init_marketplace4 = __esm(() => {
|
|
|
5818
6557
|
const src = String(p.source);
|
|
5819
6558
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
5820
6559
|
const srcDir = resolve24(dir, src);
|
|
5821
|
-
if (
|
|
5822
|
-
const hasManifest =
|
|
5823
|
-
const hasSkills =
|
|
6560
|
+
if (existsSync39(srcDir)) {
|
|
6561
|
+
const hasManifest = existsSync39(resolve24(srcDir, ".github", "plugin", "plugin.json"));
|
|
6562
|
+
const hasSkills = existsSync39(resolve24(srcDir, "skills"));
|
|
5824
6563
|
if (hasManifest || hasSkills) {
|
|
5825
6564
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
5826
6565
|
} else {
|
|
@@ -5837,12 +6576,12 @@ var init_marketplace4 = __esm(() => {
|
|
|
5837
6576
|
if (p.version)
|
|
5838
6577
|
passes.push(`plugins[${i}].version: "${p.version}"`);
|
|
5839
6578
|
}
|
|
5840
|
-
if (
|
|
6579
|
+
if (existsSync39(resolve24(dir, "README.md"))) {
|
|
5841
6580
|
passes.push("README.md exists at marketplace root");
|
|
5842
6581
|
} else {
|
|
5843
6582
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5844
6583
|
}
|
|
5845
|
-
if (
|
|
6584
|
+
if (existsSync39(resolve24(dir, "LICENSE"))) {
|
|
5846
6585
|
passes.push("LICENSE exists at marketplace root");
|
|
5847
6586
|
} else {
|
|
5848
6587
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5853,7 +6592,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
5853
6592
|
});
|
|
5854
6593
|
|
|
5855
6594
|
// src/validators/copilot/mcp.ts
|
|
5856
|
-
import { existsSync as
|
|
6595
|
+
import { existsSync as existsSync40 } from "fs";
|
|
5857
6596
|
import { resolve as resolve25 } from "path";
|
|
5858
6597
|
var copilotMcpValidator;
|
|
5859
6598
|
var init_mcp4 = __esm(() => {
|
|
@@ -5863,7 +6602,7 @@ var init_mcp4 = __esm(() => {
|
|
|
5863
6602
|
name: "Copilot MCP Config",
|
|
5864
6603
|
description: "Validates .mcp.json (referenced via mcpServers in manifest). Supports stdio and http servers.",
|
|
5865
6604
|
detect(dir) {
|
|
5866
|
-
return
|
|
6605
|
+
return existsSync40(resolve25(dir, ".mcp.json"));
|
|
5867
6606
|
},
|
|
5868
6607
|
async validate(dir, _opts) {
|
|
5869
6608
|
const errors = [];
|
|
@@ -5929,7 +6668,7 @@ var init_mcp4 = __esm(() => {
|
|
|
5929
6668
|
});
|
|
5930
6669
|
|
|
5931
6670
|
// src/validators/copilot/skill.ts
|
|
5932
|
-
import { existsSync as
|
|
6671
|
+
import { existsSync as existsSync41 } from "fs";
|
|
5933
6672
|
import { resolve as resolve26 } from "path";
|
|
5934
6673
|
var copilotSkillValidator;
|
|
5935
6674
|
var init_skill4 = __esm(() => {
|
|
@@ -5940,7 +6679,7 @@ var init_skill4 = __esm(() => {
|
|
|
5940
6679
|
name: "Copilot Skill",
|
|
5941
6680
|
description: "Validates SKILL.md (shared format). Copilot supports skills referenced via array paths in the manifest.",
|
|
5942
6681
|
detect(dir) {
|
|
5943
|
-
return
|
|
6682
|
+
return existsSync41(resolve26(dir, "SKILL.md"));
|
|
5944
6683
|
},
|
|
5945
6684
|
async validate(dir, _opts) {
|
|
5946
6685
|
const loaded = await loadSkill(dir);
|
|
@@ -6108,7 +6847,7 @@ var init_validators = __esm(() => {
|
|
|
6108
6847
|
// src/core/remote.ts
|
|
6109
6848
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6110
6849
|
import { mkdtempSync, rmSync } from "fs";
|
|
6111
|
-
import { join as
|
|
6850
|
+
import { join as join28 } from "path";
|
|
6112
6851
|
import { tmpdir } from "os";
|
|
6113
6852
|
function parseRemoteUrl(input) {
|
|
6114
6853
|
if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
|
|
@@ -6145,7 +6884,7 @@ function isGhAvailable() {
|
|
|
6145
6884
|
return ghAvailable;
|
|
6146
6885
|
}
|
|
6147
6886
|
async function cloneToTemp(parsed) {
|
|
6148
|
-
const tmpDir = mkdtempSync(
|
|
6887
|
+
const tmpDir = mkdtempSync(join28(tmpdir(), "dora-"));
|
|
6149
6888
|
const cleanup = () => {
|
|
6150
6889
|
try {
|
|
6151
6890
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -6193,19 +6932,19 @@ var exports_validate_top = {};
|
|
|
6193
6932
|
__export(exports_validate_top, {
|
|
6194
6933
|
default: () => validate_top_default
|
|
6195
6934
|
});
|
|
6196
|
-
import { existsSync as
|
|
6935
|
+
import { existsSync as existsSync43 } from "fs";
|
|
6197
6936
|
import { resolve as resolve27 } from "path";
|
|
6198
|
-
var
|
|
6937
|
+
var import_picocolors17, validate_top_default;
|
|
6199
6938
|
var init_validate_top = __esm(() => {
|
|
6200
6939
|
init_dist();
|
|
6201
6940
|
init_out();
|
|
6202
6941
|
init_validators();
|
|
6203
6942
|
init_remote();
|
|
6204
|
-
|
|
6943
|
+
import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
6205
6944
|
validate_top_default = defineCommand({
|
|
6206
6945
|
meta: {
|
|
6207
6946
|
name: "validate",
|
|
6208
|
-
description: "Auto-detect project type and run matching validators. Accepts a local path or a Git URL (e.g. https://github.com/owner/repo)"
|
|
6947
|
+
description: "Auto-detect project type and run matching validators. Accepts a local path or a Git URL (e.g. https://github.com/owner/repo). Use --for <provider>:plugin to see keyword trigger messages."
|
|
6209
6948
|
},
|
|
6210
6949
|
args: {
|
|
6211
6950
|
path: {
|
|
@@ -6241,7 +6980,7 @@ var init_validate_top = __esm(() => {
|
|
|
6241
6980
|
let cleanup;
|
|
6242
6981
|
if (remote) {
|
|
6243
6982
|
ui.info(`
|
|
6244
|
-
Cloning ${
|
|
6983
|
+
Cloning ${import_picocolors17.default.dim(args.path)}...`);
|
|
6245
6984
|
try {
|
|
6246
6985
|
const result = await cloneToTemp(remote);
|
|
6247
6986
|
fullPath = remote.subpath ? resolve27(result.dir, remote.subpath) : result.dir;
|
|
@@ -6251,14 +6990,14 @@ var init_validate_top = __esm(() => {
|
|
|
6251
6990
|
ui.fail(msg);
|
|
6252
6991
|
process.exit(1);
|
|
6253
6992
|
}
|
|
6254
|
-
if (!
|
|
6993
|
+
if (!existsSync43(fullPath)) {
|
|
6255
6994
|
cleanup();
|
|
6256
6995
|
ui.fail(`Subdirectory not found in repo: ${remote.subpath}`);
|
|
6257
6996
|
process.exit(1);
|
|
6258
6997
|
}
|
|
6259
6998
|
} else {
|
|
6260
6999
|
fullPath = resolve27(args.path);
|
|
6261
|
-
if (!
|
|
7000
|
+
if (!existsSync43(fullPath)) {
|
|
6262
7001
|
ui.fail(`Path not found: ${args.path}
|
|
6263
7002
|
|
|
6264
7003
|
Check that the path is correct and the directory exists.`);
|
|
@@ -6289,13 +7028,13 @@ Check that the path is correct and the directory exists.`);
|
|
|
6289
7028
|
` + `Available providers:
|
|
6290
7029
|
` + providers.map((p) => {
|
|
6291
7030
|
const pvs = validators.filter((v) => v.provider === p);
|
|
6292
|
-
return ` ${
|
|
6293
|
-
` + pvs.map((v) => ` \u2022 ${
|
|
7031
|
+
return ` ${import_picocolors17.default.bold(p)}
|
|
7032
|
+
` + pvs.map((v) => ` \u2022 ${import_picocolors17.default.dim(v.id)} \u2014 ${v.description}`).join(`
|
|
6294
7033
|
`);
|
|
6295
7034
|
}).join(`
|
|
6296
7035
|
`) + `
|
|
6297
7036
|
|
|
6298
|
-
Use ${
|
|
7037
|
+
Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolors17.default.dim("--for <provider:type>")} to target explicitly.`);
|
|
6299
7038
|
process.exit(1);
|
|
6300
7039
|
}
|
|
6301
7040
|
const allResults = [];
|
|
@@ -6316,7 +7055,7 @@ Use ${import_picocolors15.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
6316
7055
|
} else {
|
|
6317
7056
|
for (const { id, name, result } of allResults) {
|
|
6318
7057
|
ui.write(`
|
|
6319
|
-
${
|
|
7058
|
+
${import_picocolors17.default.bold("dora validate")} \u2014 ${import_picocolors17.default.white(name)} ${import_picocolors17.default.dim(`(${id})`)}
|
|
6320
7059
|
`);
|
|
6321
7060
|
ui.info(` Path: ${args.path}
|
|
6322
7061
|
`);
|
|
@@ -6331,7 +7070,7 @@ Use ${import_picocolors15.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
6331
7070
|
}
|
|
6332
7071
|
if (result.errors.length === 0 && result.warnings.length === 0) {
|
|
6333
7072
|
ui.write(`
|
|
6334
|
-
${
|
|
7073
|
+
${import_picocolors17.default.green("\u2713")} ${import_picocolors17.default.white("All checks passed.")}
|
|
6335
7074
|
`);
|
|
6336
7075
|
} else {
|
|
6337
7076
|
ui.info(`
|
|
@@ -6353,16 +7092,16 @@ var exports_init2 = {};
|
|
|
6353
7092
|
__export(exports_init2, {
|
|
6354
7093
|
default: () => init_default2
|
|
6355
7094
|
});
|
|
6356
|
-
import { basename as basename6, join as
|
|
7095
|
+
import { basename as basename6, join as join29 } from "path";
|
|
6357
7096
|
var {spawnSync: spawnSync5 } = globalThis.Bun;
|
|
6358
|
-
var
|
|
7097
|
+
var import_picocolors18, init_default2;
|
|
6359
7098
|
var init_init2 = __esm(() => {
|
|
6360
7099
|
init_dist();
|
|
6361
7100
|
init_out();
|
|
6362
7101
|
init_journal_config();
|
|
6363
7102
|
init_journal_remote();
|
|
6364
7103
|
init_prompt();
|
|
6365
|
-
|
|
7104
|
+
import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
6366
7105
|
init_default2 = defineCommand({
|
|
6367
7106
|
meta: {
|
|
6368
7107
|
name: "init",
|
|
@@ -6389,17 +7128,17 @@ var init_init2 = __esm(() => {
|
|
|
6389
7128
|
ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
|
|
6390
7129
|
const ghCheck = ensureGhCli();
|
|
6391
7130
|
if (!ghCheck.ok) {
|
|
6392
|
-
ui.write(` ${
|
|
7131
|
+
ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("The GitHub CLI (")}${import_picocolors18.default.bold("gh")}${import_picocolors18.default.white(") is not installed.")}
|
|
6393
7132
|
`);
|
|
6394
|
-
ui.info(` doraval uses ${
|
|
7133
|
+
ui.info(` doraval uses ${import_picocolors18.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
6395
7134
|
`);
|
|
6396
7135
|
ui.info(` Install it:
|
|
6397
7136
|
`);
|
|
6398
|
-
ui.info(` macOS: ${
|
|
6399
|
-
ui.info(` Linux: ${
|
|
6400
|
-
ui.info(` Windows: ${
|
|
7137
|
+
ui.info(` macOS: ${import_picocolors18.default.dim("brew install gh")}`);
|
|
7138
|
+
ui.info(` Linux: ${import_picocolors18.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
7139
|
+
ui.info(` Windows: ${import_picocolors18.default.dim("winget install --id GitHub.cli")}
|
|
6401
7140
|
`);
|
|
6402
|
-
ui.info(` Then authenticate: ${
|
|
7141
|
+
ui.info(` Then authenticate: ${import_picocolors18.default.dim("gh auth login")}
|
|
6403
7142
|
`);
|
|
6404
7143
|
process.exit(1);
|
|
6405
7144
|
}
|
|
@@ -6412,28 +7151,28 @@ var init_init2 = __esm(() => {
|
|
|
6412
7151
|
if (gitOwner) {
|
|
6413
7152
|
defaultRepo = `${gitOwner}/${gitOwner}.md`;
|
|
6414
7153
|
if (ghLogin && ghLogin !== gitOwner) {
|
|
6415
|
-
sourceNote = ` ${
|
|
7154
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
|
|
6416
7155
|
`;
|
|
6417
7156
|
} else {
|
|
6418
|
-
sourceNote = ` ${
|
|
7157
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from git remote)")}
|
|
6419
7158
|
`;
|
|
6420
7159
|
}
|
|
6421
7160
|
} else if (ghLogin) {
|
|
6422
7161
|
defaultRepo = `${ghLogin}/${ghLogin}.md`;
|
|
6423
|
-
sourceNote = ` ${
|
|
7162
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from your active gh account)")}
|
|
6424
7163
|
`;
|
|
6425
7164
|
} else {
|
|
6426
|
-
ui.warn(`Not logged in to GitHub. Run ${
|
|
7165
|
+
ui.warn(`Not logged in to GitHub. Run ${import_picocolors18.default.dim("gh auth login")} first.
|
|
6427
7166
|
`);
|
|
6428
7167
|
process.exit(1);
|
|
6429
7168
|
}
|
|
6430
7169
|
const existingConfig = await readConfig();
|
|
6431
7170
|
if (existingConfig?.journal.repo) {
|
|
6432
7171
|
defaultRepo = existingConfig.journal.repo;
|
|
6433
|
-
sourceNote = ` ${
|
|
7172
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from your previous journal setup)")}
|
|
6434
7173
|
`;
|
|
6435
7174
|
}
|
|
6436
|
-
ui.info(` Journal repo ${
|
|
7175
|
+
ui.info(` Journal repo ${import_picocolors18.default.dim("(owner/name)")}`);
|
|
6437
7176
|
if (sourceNote)
|
|
6438
7177
|
ui.write(sourceNote);
|
|
6439
7178
|
repo = prompt(" >", defaultRepo);
|
|
@@ -6445,11 +7184,11 @@ var init_init2 = __esm(() => {
|
|
|
6445
7184
|
}
|
|
6446
7185
|
project = sanitizeProjectName(project);
|
|
6447
7186
|
if (!repoExists(repo)) {
|
|
6448
|
-
ui.write(` ${
|
|
7187
|
+
ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("Repository")} ${import_picocolors18.default.bold(repo)} ${import_picocolors18.default.white("not found on GitHub.")}
|
|
6449
7188
|
`);
|
|
6450
7189
|
ui.info(` Create it first:
|
|
6451
7190
|
`);
|
|
6452
|
-
ui.info(` ${
|
|
7191
|
+
ui.info(` ${import_picocolors18.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
|
|
6453
7192
|
`);
|
|
6454
7193
|
process.exit(1);
|
|
6455
7194
|
}
|
|
@@ -6457,16 +7196,16 @@ var init_init2 = __esm(() => {
|
|
|
6457
7196
|
const alreadyRegistered = existing?.journal.projects[project];
|
|
6458
7197
|
const isRefresh = alreadyRegistered && args.refresh;
|
|
6459
7198
|
if (alreadyRegistered && !isRefresh) {
|
|
6460
|
-
ui.write(` ${
|
|
7199
|
+
ui.write(` ${import_picocolors18.default.yellow("\u26A0")} ${import_picocolors18.default.white("Project")} ${import_picocolors18.default.bold(project)} ${import_picocolors18.default.white("is already registered.")}
|
|
6461
7200
|
`);
|
|
6462
7201
|
ui.info(` Repo: ${existing.journal.repo}
|
|
6463
7202
|
`);
|
|
6464
|
-
ui.info(` To refresh journal files, use ${
|
|
7203
|
+
ui.info(` To refresh journal files, use ${import_picocolors18.default.dim("dora journal update")} (or ${import_picocolors18.default.dim("dora init --refresh")}).
|
|
6465
7204
|
`);
|
|
6466
7205
|
}
|
|
6467
7206
|
const journalsDir = getJournalsDir();
|
|
6468
7207
|
const remotePath = `projects/${project}.md`;
|
|
6469
|
-
const localPath =
|
|
7208
|
+
const localPath = join29(journalsDir, `${project}.md`);
|
|
6470
7209
|
const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
|
|
6471
7210
|
const config = existing ?? {
|
|
6472
7211
|
journal: { repo: effectiveRepo, projects: {} }
|
|
@@ -6477,9 +7216,9 @@ var init_init2 = __esm(() => {
|
|
|
6477
7216
|
local_path: localPath
|
|
6478
7217
|
};
|
|
6479
7218
|
ensureDoravalDirs();
|
|
6480
|
-
ui.write(` ${
|
|
7219
|
+
ui.write(` ${import_picocolors18.default.dim(import_picocolors18.default.gray("Fetching journal files from"))} ${import_picocolors18.default.gray(effectiveRepo)}${import_picocolors18.default.dim(import_picocolors18.default.gray("..."))}
|
|
6481
7220
|
`);
|
|
6482
|
-
const globalDest =
|
|
7221
|
+
const globalDest = join29(journalsDir, "global.md");
|
|
6483
7222
|
const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
|
|
6484
7223
|
let wroteGlobal;
|
|
6485
7224
|
if (!refreshGlobalRes.ok) {
|
|
@@ -6496,7 +7235,7 @@ var init_init2 = __esm(() => {
|
|
|
6496
7235
|
if (wroteGlobal) {
|
|
6497
7236
|
ui.success("global.md");
|
|
6498
7237
|
} else {
|
|
6499
|
-
ui.write(` ${
|
|
7238
|
+
ui.write(` ${import_picocolors18.default.dim("\xB7")} global.md ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
6500
7239
|
await Bun.write(globalDest, `# Global Journal
|
|
6501
7240
|
|
|
6502
7241
|
Cross-project principles.
|
|
@@ -6518,7 +7257,7 @@ Cross-project principles.
|
|
|
6518
7257
|
if (wroteProject) {
|
|
6519
7258
|
ui.success(remotePath);
|
|
6520
7259
|
} else {
|
|
6521
|
-
ui.write(` ${
|
|
7260
|
+
ui.write(` ${import_picocolors18.default.dim("\xB7")} ${remotePath} ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
6522
7261
|
await Bun.write(localPath, `# ${project} Journal
|
|
6523
7262
|
|
|
6524
7263
|
Project-specific decisions.
|
|
@@ -6526,13 +7265,13 @@ Project-specific decisions.
|
|
|
6526
7265
|
}
|
|
6527
7266
|
await writeConfig(config);
|
|
6528
7267
|
ui.write(`
|
|
6529
|
-
${
|
|
7268
|
+
${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Journal ready for project")} ${import_picocolors18.default.bold(import_picocolors18.default.white(project))}.
|
|
6530
7269
|
`);
|
|
6531
7270
|
const existingAgent = (await readConfig())?.agent;
|
|
6532
7271
|
if (existingAgent?.command) {
|
|
6533
|
-
ui.write(` ${
|
|
7272
|
+
ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent (already configured)"))}
|
|
6534
7273
|
`);
|
|
6535
|
-
ui.write(` Current: ${
|
|
7274
|
+
ui.write(` Current: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.command))} template: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.prompt_template || "(default)"))}
|
|
6536
7275
|
`);
|
|
6537
7276
|
const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
|
|
6538
7277
|
if (!/^y/i.test(String(change))) {
|
|
@@ -6542,16 +7281,16 @@ Project-specific decisions.
|
|
|
6542
7281
|
if (existingAgent)
|
|
6543
7282
|
cfg.agent = existingAgent;
|
|
6544
7283
|
await writeConfig(cfg);
|
|
6545
|
-
ui.write(` ${
|
|
7284
|
+
ui.write(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Try:")} ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add "short decision"'))}
|
|
6546
7285
|
`);
|
|
6547
7286
|
process.exit(0);
|
|
6548
7287
|
return;
|
|
6549
7288
|
}
|
|
6550
7289
|
ui.blank();
|
|
6551
7290
|
} else {
|
|
6552
|
-
ui.write(` ${
|
|
7291
|
+
ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent for journal add"))}
|
|
6553
7292
|
`);
|
|
6554
|
-
ui.info(` When configured, ${
|
|
7293
|
+
ui.info(` When configured, ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
|
|
6555
7294
|
`);
|
|
6556
7295
|
}
|
|
6557
7296
|
const common = [
|
|
@@ -6570,7 +7309,7 @@ Project-specific decisions.
|
|
|
6570
7309
|
}
|
|
6571
7310
|
}
|
|
6572
7311
|
let agentCmd = detected || "claude";
|
|
6573
|
-
ui.write(` Detected / default agent command: ${
|
|
7312
|
+
ui.write(` Detected / default agent command: ${import_picocolors18.default.dim(import_picocolors18.default.gray(agentCmd))}`);
|
|
6574
7313
|
agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
|
|
6575
7314
|
let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
|
|
6576
7315
|
ui.info(` Prompt template (use {{prompt}} placeholder):`);
|
|
@@ -6582,11 +7321,11 @@ Project-specific decisions.
|
|
|
6582
7321
|
};
|
|
6583
7322
|
await writeConfig(finalConfig);
|
|
6584
7323
|
ui.write(`
|
|
6585
|
-
${
|
|
7324
|
+
${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Agent configured.")}
|
|
6586
7325
|
`);
|
|
6587
|
-
ui.info(` Re-run ${
|
|
7326
|
+
ui.info(` Re-run ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora init"))} anytime to change it.
|
|
6588
7327
|
`);
|
|
6589
|
-
ui.info(` Next: ${
|
|
7328
|
+
ui.info(` Next: ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))}, ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal list"))}, or ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal update"))}.
|
|
6590
7329
|
`);
|
|
6591
7330
|
process.exit(0);
|
|
6592
7331
|
}
|
|
@@ -6595,7 +7334,7 @@ Project-specific decisions.
|
|
|
6595
7334
|
|
|
6596
7335
|
// src/core/update.ts
|
|
6597
7336
|
import { resolve as resolve28 } from "path";
|
|
6598
|
-
import { homedir as
|
|
7337
|
+
import { homedir as homedir3 } from "os";
|
|
6599
7338
|
function normalizePath(p) {
|
|
6600
7339
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6601
7340
|
}
|
|
@@ -6773,14 +7512,14 @@ async function readMarker() {
|
|
|
6773
7512
|
async function writeMarker(marker) {
|
|
6774
7513
|
try {
|
|
6775
7514
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
6776
|
-
const { dirname:
|
|
6777
|
-
await mkdir(
|
|
7515
|
+
const { dirname: dirname8 } = await import("path");
|
|
7516
|
+
await mkdir(dirname8(MARKER_PATH), { recursive: true });
|
|
6778
7517
|
await writeFile(MARKER_PATH, JSON.stringify(marker, null, 2));
|
|
6779
7518
|
} catch {}
|
|
6780
7519
|
}
|
|
6781
7520
|
var MARKER_PATH;
|
|
6782
7521
|
var init_update2 = __esm(() => {
|
|
6783
|
-
MARKER_PATH = resolve28(
|
|
7522
|
+
MARKER_PATH = resolve28(homedir3(), ".doraval", "install.json");
|
|
6784
7523
|
});
|
|
6785
7524
|
|
|
6786
7525
|
// src/cli/commands/update.ts
|
|
@@ -6789,7 +7528,7 @@ __export(exports_update2, {
|
|
|
6789
7528
|
default: () => update_default2
|
|
6790
7529
|
});
|
|
6791
7530
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
6792
|
-
import { homedir as
|
|
7531
|
+
import { homedir as homedir4 } from "os";
|
|
6793
7532
|
import { fileURLToPath } from "url";
|
|
6794
7533
|
import { realpath, access } from "fs/promises";
|
|
6795
7534
|
async function confirmUpdate() {
|
|
@@ -6835,7 +7574,7 @@ var init_update3 = __esm(() => {
|
|
|
6835
7574
|
entrypoint,
|
|
6836
7575
|
argv: process.argv,
|
|
6837
7576
|
env: process.env,
|
|
6838
|
-
homeDir:
|
|
7577
|
+
homeDir: homedir4(),
|
|
6839
7578
|
realpath: (p) => realpath(p),
|
|
6840
7579
|
exists: async (p) => {
|
|
6841
7580
|
try {
|
|
@@ -6926,16 +7665,16 @@ var exports_providers = {};
|
|
|
6926
7665
|
__export(exports_providers, {
|
|
6927
7666
|
default: () => providers_default
|
|
6928
7667
|
});
|
|
6929
|
-
var
|
|
7668
|
+
var import_picocolors19, providers_default;
|
|
6930
7669
|
var init_providers2 = __esm(() => {
|
|
6931
7670
|
init_dist();
|
|
6932
7671
|
init_out();
|
|
6933
7672
|
init_spec();
|
|
6934
|
-
|
|
7673
|
+
import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
6935
7674
|
providers_default = defineCommand({
|
|
6936
7675
|
meta: {
|
|
6937
7676
|
name: "providers",
|
|
6938
|
-
description: "List supported providers and their packaging details"
|
|
7677
|
+
description: "List supported providers and their packaging details (including keyword discovery)"
|
|
6939
7678
|
},
|
|
6940
7679
|
args: {
|
|
6941
7680
|
json: {
|
|
@@ -6956,14 +7695,136 @@ var init_providers2 = __esm(() => {
|
|
|
6956
7695
|
for (const id of supportedProviders) {
|
|
6957
7696
|
const spec = getProviderSpec(id);
|
|
6958
7697
|
ui.write(`
|
|
6959
|
-
${
|
|
7698
|
+
${import_picocolors19.default.bold(id)} \u2014 ${spec.name}`);
|
|
6960
7699
|
ui.info(` Manifest: ${spec.manifestPath}`);
|
|
6961
7700
|
ui.info(` Marketplace: ${spec.marketplacePath}`);
|
|
6962
7701
|
ui.info(` MCP: ${spec.mcpFilename}`);
|
|
6963
|
-
ui.info(`
|
|
7702
|
+
ui.info(` Keywords in plugin.json: supported \u2014 If users mention any of these keywords, your plugin will get triggered`);
|
|
7703
|
+
ui.info(` Example: doraval validate . --for ${id}:plugin`);
|
|
6964
7704
|
}
|
|
6965
7705
|
ui.write(`
|
|
6966
7706
|
Use --json for machine-readable output.`);
|
|
7707
|
+
ui.write(` Tip: Add a "keywords" array to your plugin manifest for better agent discovery.`);
|
|
7708
|
+
process.exit(0);
|
|
7709
|
+
}
|
|
7710
|
+
});
|
|
7711
|
+
});
|
|
7712
|
+
|
|
7713
|
+
// src/cli/commands/completion.ts
|
|
7714
|
+
var exports_completion = {};
|
|
7715
|
+
__export(exports_completion, {
|
|
7716
|
+
default: () => completion_default
|
|
7717
|
+
});
|
|
7718
|
+
var commands, subCommands, completion_default;
|
|
7719
|
+
var init_completion = __esm(() => {
|
|
7720
|
+
init_dist();
|
|
7721
|
+
commands = [
|
|
7722
|
+
"validate",
|
|
7723
|
+
"init",
|
|
7724
|
+
"bump",
|
|
7725
|
+
"update",
|
|
7726
|
+
"providers",
|
|
7727
|
+
"skill",
|
|
7728
|
+
"journal",
|
|
7729
|
+
"claude",
|
|
7730
|
+
"codex",
|
|
7731
|
+
"cursor",
|
|
7732
|
+
"copilot"
|
|
7733
|
+
];
|
|
7734
|
+
subCommands = {
|
|
7735
|
+
skill: ["validate", "drift", "judge"],
|
|
7736
|
+
journal: ["init", "list", "context", "hook", "update", "add", "sync"],
|
|
7737
|
+
hook: ["enable", "disable", "status"],
|
|
7738
|
+
claude: ["new", "bump"],
|
|
7739
|
+
codex: ["new", "bump"],
|
|
7740
|
+
cursor: ["new", "bump"],
|
|
7741
|
+
copilot: ["new", "bump"]
|
|
7742
|
+
};
|
|
7743
|
+
completion_default = defineCommand({
|
|
7744
|
+
meta: {
|
|
7745
|
+
name: "completion",
|
|
7746
|
+
description: "Generate shell completion scripts (bash, zsh, fish)"
|
|
7747
|
+
},
|
|
7748
|
+
args: {
|
|
7749
|
+
shell: {
|
|
7750
|
+
type: "positional",
|
|
7751
|
+
description: "Shell to generate completion for (bash | zsh | fish)",
|
|
7752
|
+
required: true
|
|
7753
|
+
}
|
|
7754
|
+
},
|
|
7755
|
+
run({ args }) {
|
|
7756
|
+
const shell = String(args.shell).toLowerCase();
|
|
7757
|
+
if (shell === "bash") {
|
|
7758
|
+
console.log(`# doraval bash completion
|
|
7759
|
+
_doraval_completions() {
|
|
7760
|
+
local cur prev
|
|
7761
|
+
COMPREPLY=()
|
|
7762
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
7763
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
7764
|
+
|
|
7765
|
+
if [ $COMP_CWORD -eq 1 ]; then
|
|
7766
|
+
COMPREPLY=( $(compgen -W "${commands.join(" ")}" -- "$cur") )
|
|
7767
|
+
elif [ $COMP_CWORD -eq 2 ]; then
|
|
7768
|
+
case "$prev" in
|
|
7769
|
+
skill) COMPREPLY=( $(compgen -W "${subCommands.skill.join(" ")}" -- "$cur") ) ;;
|
|
7770
|
+
journal) COMPREPLY=( $(compgen -W "${subCommands.journal.join(" ")}" -- "$cur") ) ;;
|
|
7771
|
+
hook) COMPREPLY=( $(compgen -W "${subCommands.hook.join(" ")}" -- "$cur") ) ;;
|
|
7772
|
+
claude|codex|cursor|copilot) COMPREPLY=( $(compgen -W "${subCommands.claude.join(" ")}" -- "$cur") ) ;;
|
|
7773
|
+
esac
|
|
7774
|
+
fi
|
|
7775
|
+
}
|
|
7776
|
+
complete -F _doraval_completions doraval
|
|
7777
|
+
`);
|
|
7778
|
+
} else if (shell === "zsh") {
|
|
7779
|
+
console.log(`# doraval zsh completion
|
|
7780
|
+
#compdef doraval
|
|
7781
|
+
|
|
7782
|
+
_doraval() {
|
|
7783
|
+
local -a commands sub
|
|
7784
|
+
commands=(validate init bump update providers skill journal claude codex cursor copilot)
|
|
7785
|
+
_arguments -C \\
|
|
7786
|
+
'1: :->cmd' \\
|
|
7787
|
+
'*::arg:->args'
|
|
7788
|
+
|
|
7789
|
+
case $state in
|
|
7790
|
+
cmd)
|
|
7791
|
+
_describe 'command' commands
|
|
7792
|
+
;;
|
|
7793
|
+
args)
|
|
7794
|
+
case $words[1] in
|
|
7795
|
+
skill)
|
|
7796
|
+
_describe 'subcommand' (validate drift judge)
|
|
7797
|
+
;;
|
|
7798
|
+
journal)
|
|
7799
|
+
_describe 'subcommand' (init list context hook update add sync)
|
|
7800
|
+
;;
|
|
7801
|
+
hook)
|
|
7802
|
+
_describe 'subcommand' (enable disable status)
|
|
7803
|
+
;;
|
|
7804
|
+
claude|codex|cursor|copilot)
|
|
7805
|
+
_describe 'subcommand' (new bump)
|
|
7806
|
+
;;
|
|
7807
|
+
esac
|
|
7808
|
+
;;
|
|
7809
|
+
esac
|
|
7810
|
+
}
|
|
7811
|
+
|
|
7812
|
+
_doraval "$@"
|
|
7813
|
+
`);
|
|
7814
|
+
} else if (shell === "fish") {
|
|
7815
|
+
console.log(`# doraval fish completion
|
|
7816
|
+
complete -c doraval -f
|
|
7817
|
+
complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal claude codex cursor copilot'
|
|
7818
|
+
|
|
7819
|
+
complete -c doraval -n '__fish_seen_subcommand_from skill' -a 'validate drift judge'
|
|
7820
|
+
complete -c doraval -n '__fish_seen_subcommand_from journal' -a 'init list context hook update add sync'
|
|
7821
|
+
complete -c doraval -n '__fish_seen_subcommand_from hook' -a 'enable disable status'
|
|
7822
|
+
complete -c doraval -n '__fish_seen_subcommand_from claude codex cursor copilot' -a 'new bump'
|
|
7823
|
+
`);
|
|
7824
|
+
} else {
|
|
7825
|
+
console.error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
7826
|
+
process.exit(1);
|
|
7827
|
+
}
|
|
6967
7828
|
process.exit(0);
|
|
6968
7829
|
}
|
|
6969
7830
|
});
|
|
@@ -6972,7 +7833,7 @@ var init_providers2 = __esm(() => {
|
|
|
6972
7833
|
// src/cli/index.ts
|
|
6973
7834
|
init_dist();
|
|
6974
7835
|
var import__package = __toESM(require_package(), 1);
|
|
6975
|
-
var
|
|
7836
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
6976
7837
|
var skill = defineCommand({
|
|
6977
7838
|
meta: {
|
|
6978
7839
|
name: "skill",
|
|
@@ -6984,6 +7845,9 @@ var skill = defineCommand({
|
|
|
6984
7845
|
judge: () => Promise.resolve().then(() => (init_judge(), exports_judge)).then((m) => m.default)
|
|
6985
7846
|
},
|
|
6986
7847
|
run() {
|
|
7848
|
+
const cliArgs = process.argv.slice(2);
|
|
7849
|
+
if (cliArgs[0] === "skill" && cliArgs.length > 1)
|
|
7850
|
+
return;
|
|
6987
7851
|
showUsage(skill);
|
|
6988
7852
|
}
|
|
6989
7853
|
});
|
|
@@ -6995,11 +7859,16 @@ var journal = defineCommand({
|
|
|
6995
7859
|
subCommands: {
|
|
6996
7860
|
init: () => Promise.resolve().then(() => (init_init(), exports_init)).then((m) => m.default),
|
|
6997
7861
|
list: () => Promise.resolve().then(() => (init_list(), exports_list)).then((m) => m.default),
|
|
7862
|
+
context: () => Promise.resolve().then(() => (init_context(), exports_context)).then((m) => m.default),
|
|
7863
|
+
hook: () => Promise.resolve().then(() => (init_hook(), exports_hook)).then((m) => m.default),
|
|
6998
7864
|
update: () => Promise.resolve().then(() => (init_update(), exports_update)).then((m) => m.default),
|
|
6999
7865
|
add: () => Promise.resolve().then(() => (init_add(), exports_add)).then((m) => m.default),
|
|
7000
7866
|
sync: () => Promise.resolve().then(() => (init_sync(), exports_sync)).then((m) => m.default)
|
|
7001
7867
|
},
|
|
7002
7868
|
run() {
|
|
7869
|
+
const cliArgs = process.argv.slice(2);
|
|
7870
|
+
if (cliArgs[0] === "journal" && cliArgs.length > 1)
|
|
7871
|
+
return;
|
|
7003
7872
|
showUsage(journal);
|
|
7004
7873
|
}
|
|
7005
7874
|
});
|
|
@@ -7013,6 +7882,9 @@ var claude = defineCommand({
|
|
|
7013
7882
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7014
7883
|
},
|
|
7015
7884
|
run() {
|
|
7885
|
+
const cliArgs = process.argv.slice(2);
|
|
7886
|
+
if (cliArgs[0] === "claude" && cliArgs.length > 1)
|
|
7887
|
+
return;
|
|
7016
7888
|
showUsage(claude);
|
|
7017
7889
|
}
|
|
7018
7890
|
});
|
|
@@ -7026,6 +7898,9 @@ var codex = defineCommand({
|
|
|
7026
7898
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7027
7899
|
},
|
|
7028
7900
|
run() {
|
|
7901
|
+
const cliArgs = process.argv.slice(2);
|
|
7902
|
+
if (cliArgs[0] === "codex" && cliArgs.length > 1)
|
|
7903
|
+
return;
|
|
7029
7904
|
showUsage(codex);
|
|
7030
7905
|
}
|
|
7031
7906
|
});
|
|
@@ -7039,6 +7914,9 @@ var cursor = defineCommand({
|
|
|
7039
7914
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7040
7915
|
},
|
|
7041
7916
|
run() {
|
|
7917
|
+
const cliArgs = process.argv.slice(2);
|
|
7918
|
+
if (cliArgs[0] === "cursor" && cliArgs.length > 1)
|
|
7919
|
+
return;
|
|
7042
7920
|
showUsage(cursor);
|
|
7043
7921
|
}
|
|
7044
7922
|
});
|
|
@@ -7052,9 +7930,38 @@ var copilot = defineCommand({
|
|
|
7052
7930
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7053
7931
|
},
|
|
7054
7932
|
run() {
|
|
7933
|
+
const cliArgs = process.argv.slice(2);
|
|
7934
|
+
if (cliArgs[0] === "copilot" && cliArgs.length > 1)
|
|
7935
|
+
return;
|
|
7055
7936
|
showUsage(copilot);
|
|
7056
7937
|
}
|
|
7057
7938
|
});
|
|
7939
|
+
var ui2 = defineCommand({
|
|
7940
|
+
meta: {
|
|
7941
|
+
name: "ui",
|
|
7942
|
+
description: "Launch the local doraval web dashboard (no more typing commands for common tasks)"
|
|
7943
|
+
},
|
|
7944
|
+
args: {
|
|
7945
|
+
port: {
|
|
7946
|
+
type: "string",
|
|
7947
|
+
description: "Port to run the local UI server on (default 3737)",
|
|
7948
|
+
default: "3737"
|
|
7949
|
+
},
|
|
7950
|
+
open: {
|
|
7951
|
+
type: "boolean",
|
|
7952
|
+
description: "Automatically open the dashboard in your browser",
|
|
7953
|
+
default: true
|
|
7954
|
+
},
|
|
7955
|
+
host: {
|
|
7956
|
+
type: "string",
|
|
7957
|
+
description: "Host to bind (default 127.0.0.1 for local only)",
|
|
7958
|
+
default: "127.0.0.1"
|
|
7959
|
+
}
|
|
7960
|
+
},
|
|
7961
|
+
async run({ args }) {
|
|
7962
|
+
await Promise.resolve().then(() => (init_ui(), exports_ui)).then((m) => m.default.run({ args }));
|
|
7963
|
+
}
|
|
7964
|
+
});
|
|
7058
7965
|
var doraemonArt = `
|
|
7059
7966
|
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28E0\u28E4\u28F4\u28F6\u28F6\u28F6\u28F6\u28F6\u2836\u28F6\u28E4\u28E4\u28C0\u2800\u2800\u2800\u2800\u2800\u2800
|
|
7060
7967
|
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28E4\u28FE\u28FF\u28FF\u28FF\u2801\u2800\u2880\u2808\u28BF\u2880\u28C0\u2800\u2839\u28FF\u28FF\u28FF\u28E6\u28C4\u2800\u2800\u2800
|
|
@@ -7079,17 +7986,24 @@ var main = defineCommand({
|
|
|
7079
7986
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default),
|
|
7080
7987
|
update: () => Promise.resolve().then(() => (init_update3(), exports_update2)).then((m) => m.default),
|
|
7081
7988
|
providers: () => Promise.resolve().then(() => (init_providers2(), exports_providers)).then((m) => m.default),
|
|
7989
|
+
completion: () => Promise.resolve().then(() => (init_completion(), exports_completion)).then((m) => m.default),
|
|
7082
7990
|
skill: () => Promise.resolve(skill),
|
|
7083
7991
|
journal: () => Promise.resolve(journal),
|
|
7084
7992
|
claude: () => Promise.resolve(claude),
|
|
7085
7993
|
codex: () => Promise.resolve(codex),
|
|
7086
7994
|
cursor: () => Promise.resolve(cursor),
|
|
7087
|
-
copilot: () => Promise.resolve(copilot)
|
|
7995
|
+
copilot: () => Promise.resolve(copilot),
|
|
7996
|
+
ui: () => Promise.resolve(ui2)
|
|
7088
7997
|
},
|
|
7089
7998
|
run() {
|
|
7090
|
-
|
|
7091
|
-
|
|
7999
|
+
const cliArgs = process.argv.slice(2);
|
|
8000
|
+
if (cliArgs.length > 0)
|
|
8001
|
+
return;
|
|
8002
|
+
if (process.stdout.isTTY) {
|
|
8003
|
+
console.error(`
|
|
8004
|
+
` + import_picocolors20.default.blue(doraemonArt) + `
|
|
7092
8005
|
`);
|
|
8006
|
+
}
|
|
7093
8007
|
showUsage(main);
|
|
7094
8008
|
}
|
|
7095
8009
|
});
|