@astryxdesign/cli 0.7.2 → 0.7.3-canary.06872b4
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/README.md +99 -66
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +115 -3
- package/api/build/blocks/blocks.d.mts +42 -0
- package/api/build/blocks/blocks.mjs +169 -0
- package/api/build/blocks/blocks.test.mjs +108 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/kit/answer.d.mts +14 -0
- package/api/build/kit/answer.mjs +85 -0
- package/api/build/kit/answer.test.mjs +38 -0
- package/api/build/kit/kind.d.mts +25 -0
- package/api/build/kit/kind.mjs +170 -0
- package/api/build/kit/kit.d.mts +2 -4
- package/api/build/kit/kit.mjs +8 -398
- package/api/build/kit/rank.d.mts +3 -32
- package/api/build/kit/rank.mjs +25 -342
- package/api/build/kit/rank.test.mjs +2 -1
- package/api/build/kit/signals.d.mts +60 -0
- package/api/build/kit/signals.mjs +284 -0
- package/api/build/kit/start.d.mts +32 -0
- package/api/build/kit/start.mjs +163 -0
- package/api/build/kit/words.d.mts +72 -0
- package/api/build/kit/words.mjs +202 -0
- package/api/build/tree.d.mts +20 -0
- package/api/build/tree.mjs +52 -0
- package/api/build/tree.test.mjs +177 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/component.mjs +16 -2
- package/api/component/component.test.mjs +15 -0
- package/api/discover/_adapter.mjs +36 -9
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/docs/_adapter.mjs +17 -5
- package/api/docs/docs.test.mjs +7 -1
- package/api/doctor/doctor.mjs +86 -12
- package/api/doctor/doctor.test.mjs +28 -3
- package/api/init/init.doc.mjs +20 -6
- package/api/init/init.test.mjs +18 -2
- package/api/init/init.type.d.mts +3 -3
- package/api/init/init.type.mjs +3 -3
- package/api/init/remove/remove.mjs +17 -4
- package/api/init/remove/remove.test.mjs +114 -2
- package/api/init/run/run.mjs +69 -6
- package/api/integration/authoring-checks.mjs +83 -9
- package/api/integration/pack-check.mjs +123 -4
- package/api/integration/pack-check.test.mjs +87 -0
- package/api/integration/validate-integration-case-disk.test.mjs +111 -0
- package/api/integration/validate-integration-gaps.test.mjs +1964 -0
- package/api/integration/validate-integration.d.mts +11 -2
- package/api/integration/validate-integration.mjs +226 -32
- package/api/integration/validate-integration.type.d.mts +58 -1
- package/api/integration/validate-integration.type.mjs +45 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +66 -2
- package/api/search/_adapter.mjs +118 -10
- package/api/search/_candidate-score.d.mts +40 -0
- package/api/search/_candidate-score.mjs +238 -0
- package/api/search/_cards.json +6098 -0
- package/api/search/_compound.d.mts +50 -0
- package/api/search/_compound.mjs +170 -0
- package/api/search/_doc-searcher.d.mts +4 -0
- package/api/search/_doc-searcher.mjs +23 -0
- package/api/search/_doc-searcher.test.mjs +36 -0
- package/api/search/_doc-searcher.weights.json +1 -0
- package/api/search/_doc-text.d.mts +30 -0
- package/api/search/_doc-text.mjs +87 -0
- package/api/search/_entries.d.mts +52 -0
- package/api/search/_entries.mjs +158 -0
- package/api/search/_features.d.mts +35 -0
- package/api/search/_features.mjs +155 -0
- package/api/search/_gather-components.d.mts +26 -0
- package/api/search/_gather-components.mjs +284 -0
- package/api/search/_gather-docs.d.mts +20 -0
- package/api/search/_gather-docs.mjs +254 -0
- package/api/search/_gather-hooks.d.mts +12 -0
- package/api/search/_gather-hooks.mjs +52 -0
- package/api/search/_gather-templates.d.mts +25 -0
- package/api/search/_gather-templates.mjs +107 -0
- package/api/search/_lexical.d.mts +135 -0
- package/api/search/_lexical.mjs +351 -0
- package/api/search/_main-searcher.d.mts +19 -0
- package/api/search/_main-searcher.mjs +115 -0
- package/api/search/_main-searcher.test.mjs +106 -0
- package/api/search/_main-searcher.weights.json +1 -0
- package/api/search/_module-doc.d.mts +32 -0
- package/api/search/_module-doc.mjs +78 -0
- package/api/search/_order.d.mts +32 -0
- package/api/search/_order.mjs +162 -0
- package/api/search/_order.test.mjs +72 -0
- package/api/search/_priority.d.mts +50 -0
- package/api/search/_priority.mjs +139 -0
- package/api/search/_query-words.d.mts +25 -0
- package/api/search/_query-words.mjs +92 -0
- package/api/search/_query.d.mts +65 -0
- package/api/search/_query.mjs +176 -0
- package/api/search/_result.d.mts +97 -0
- package/api/search/_result.mjs +150 -0
- package/api/search/_score.d.mts +60 -0
- package/api/search/_score.mjs +283 -0
- package/api/search/_synonyms.d.mts +21 -0
- package/api/search/_synonyms.mjs +67 -0
- package/api/search/_template-searcher.d.mts +4 -0
- package/api/search/_template-searcher.mjs +23 -0
- package/api/search/_template-searcher.test.mjs +40 -0
- package/api/search/_template-searcher.weights.json +1 -0
- package/api/search/_titles.d.mts +33 -0
- package/api/search/_titles.mjs +65 -0
- package/api/search/_words.d.mts +64 -0
- package/api/search/_words.mjs +157 -0
- package/api/search/search.d.mts +22 -305
- package/api/search/search.doc.mjs +7 -5
- package/api/search/search.mjs +53 -2483
- package/api/search/search.type.d.mts +6 -1
- package/api/search/search.type.mjs +6 -1
- package/api/search/tree.d.mts +15 -0
- package/api/search/tree.mjs +40 -0
- package/api/search/tree.test.mjs +66 -0
- package/api/search/tree.weights.json +1 -0
- package/api/theme/build/build.packed-old-core.test.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +57 -9
- package/api/upgrade/_adapter.mjs +198 -28
- package/api/upgrade/registry/registry.d.mts +10 -4
- package/api/upgrade/registry/registry.mjs +33 -16
- package/api/upgrade/registry/registry.test.mjs +85 -0
- package/api/upgrade/run/run.d.mts +4 -2
- package/api/upgrade/run/run.mjs +13 -2
- package/api/upgrade/run/run.test.mjs +417 -3
- package/api/upgrade/upgrade.doc.mjs +10 -5
- package/api/upgrade/upgrade.mjs +13 -2
- package/api/upgrade/upgrade.test.mjs +194 -0
- package/api/upgrade/upgrade.type.d.mts +5 -3
- package/api/upgrade/upgrade.type.mjs +8 -6
- package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
- package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
- package/assets/docs/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +16 -37
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +6 -2
- package/assets/templates/themes/butter/butterTheme.ts +4 -2
- package/assets/templates/themes/stone/stoneTheme.ts +4 -2
- package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
- package/authoring/debug/parse.d.mts +1 -1
- package/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/commands/doctor.mjs +30 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- package/clients/cli/commands/init.behavior.test.mjs +230 -1
- package/clients/cli/commands/init.doc.mjs +28 -9
- package/clients/cli/commands/init.flag-help.test.mjs +1 -0
- package/clients/cli/commands/init.mjs +2 -2
- package/clients/cli/commands/outside-project.test.mjs +2 -1
- package/clients/cli/commands/setup-nudge.test.mjs +61 -0
- package/clients/cli/commands/upgrade.doc.mjs +4 -2
- package/clients/cli/commands/upgrade.mjs +9 -2
- package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
- package/clients/cli/index.mjs +19 -1
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
- package/foundation/agent-docs/agent-doc-state.mjs +35 -8
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1010 -99
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +678 -35
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- package/foundation/discovery/component-discovery.d.mts +37 -0
- package/foundation/discovery/component-discovery.mjs +80 -5
- package/foundation/discovery/component-loader.d.mts +1 -1
- package/foundation/discovery/docs-discovery.mjs +14 -1
- package/foundation/discovery/module-exports.d.mts +41 -0
- package/foundation/discovery/module-exports.mjs +253 -0
- package/foundation/discovery/template-adapter.mjs +18 -1
- package/foundation/discovery/template-needs.mjs +1 -44
- package/foundation/discovery/theme-discovery.mjs +147 -3
- package/foundation/doc-compiler/doc-loads.test.mjs +8 -0
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- package/foundation/fs/paths.d.mts +70 -8
- package/foundation/fs/paths.mjs +556 -38
- package/foundation/fs/paths.test.mjs +430 -54
- package/foundation/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- package/foundation/integrations/computer-integrations.d.mts +6 -3
- package/foundation/integrations/computer-integrations.mjs +38 -9
- package/foundation/integrations/contribution-inventory.d.mts +15 -20
- package/foundation/integrations/contribution-inventory.mjs +41 -5
- package/foundation/integrations/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- package/foundation/release/run-context.mjs +14 -0
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/route/route.d.mts +285 -0
- package/foundation/route/route.mjs +324 -0
- package/foundation/route/route.test.mjs +153 -0
- package/foundation/runtime/hosted-runtime.test.mjs +403 -0
- package/foundation/runtime/locked-versions.d.mts +32 -0
- package/foundation/runtime/locked-versions.mjs +65 -0
- package/foundation/runtime/package-root.d.mts +91 -0
- package/foundation/runtime/package-root.mjs +174 -0
- package/foundation/runtime/package-set.d.mts +62 -0
- package/foundation/runtime/package-set.mjs +130 -0
- package/foundation/runtime/package-set.test.mjs +432 -0
- package/host/door-conformance.test.mjs +274 -0
- package/host/index.d.ts +116 -0
- package/host/index.mjs +295 -0
- package/host/index.test.mjs +302 -0
- package/package.json +14 -9
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file One run of the CLI, after the Node version gate: choose who answers
|
|
5
|
+
* the folder, hand the run off when that is another release (spec AST-081
|
|
6
|
+
* FR2, FR9), then dispatch the command.
|
|
7
|
+
*
|
|
8
|
+
* @input The arguments after `astryx`, read from `process.argv`; the CLI
|
|
9
|
+
* package root; and, for a run a programmatic host started, the reason the
|
|
10
|
+
* host declined the project's own CLI.
|
|
11
|
+
* @output The command's effects. The run owns the process: its exit status is
|
|
12
|
+
* `process.exitCode`, unless a hand-off or a failure exits the process.
|
|
13
|
+
* @position clients/cli — called by `bin/astryx.mjs` and by the host entry
|
|
14
|
+
* (`host/index.mjs`).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Run the CLI once in this process.
|
|
19
|
+
*
|
|
20
|
+
* Every import here is dynamic: modules that load `styleText` must load only
|
|
21
|
+
* after the bin's Node version gate, and the release choice must run before
|
|
22
|
+
* the command modules load, so a run handed to another release loads none of
|
|
23
|
+
* them.
|
|
24
|
+
*
|
|
25
|
+
* @param {{cliRoot: string, argv: string[], declinedProjectCli?: string}} input
|
|
26
|
+
* @returns {Promise<import('../../host/index.js').HostedCliResult | undefined>}
|
|
27
|
+
* resolves when the command has finished; with another release's result
|
|
28
|
+
* when, in a hosted run, that release's host entry answered
|
|
29
|
+
*/
|
|
30
|
+
export async function runCli({cliRoot, argv, declinedProjectCli}) {
|
|
31
|
+
const {startRun} = await import('../../foundation/release/handoff.mjs');
|
|
32
|
+
const answered = await startRun({cliRoot, argv, declinedProjectCli});
|
|
33
|
+
if (answered) return answered;
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
program,
|
|
37
|
+
loadProjectDebugHandler,
|
|
38
|
+
loadProjectForHelp,
|
|
39
|
+
handleVersionJsonPreflight,
|
|
40
|
+
} = await import('./index.mjs');
|
|
41
|
+
const {isJsonMode, toErrorEnvelope} =
|
|
42
|
+
await import('../../foundation/response/json.mjs');
|
|
43
|
+
const {handleCommanderError} = await import('./lib/json-shim.mjs');
|
|
44
|
+
const {setOutcome} = await import('../../foundation/debug/index.mjs');
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Top-level error boundary (contract guarantee #4): an uncaught throw must
|
|
48
|
+
* never reach a --json consumer as a raw stack trace. If we're in JSON mode,
|
|
49
|
+
* convert it to the contract's error envelope on stdout and exit 1;
|
|
50
|
+
* otherwise preserve the normal human-facing behavior.
|
|
51
|
+
*
|
|
52
|
+
* We detect JSON mode two ways: the global flag (set once root options are
|
|
53
|
+
* parsed) and a raw argv check, since a throw can occur during
|
|
54
|
+
* parsing/command load — before the preAction hook engages the flag.
|
|
55
|
+
*/
|
|
56
|
+
function inJsonMode() {
|
|
57
|
+
return isJsonMode() || process.argv.slice(2).includes('--json');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @param {unknown} err */
|
|
61
|
+
function handleFatal(err) {
|
|
62
|
+
// CommanderError (parse errors, --help, unknown command) routes
|
|
63
|
+
// through the JSON shim so that a --json consumer always gets a
|
|
64
|
+
// valid envelope and the right exit code. handleCommanderError
|
|
65
|
+
// calls process.exit when it owns the error.
|
|
66
|
+
if (handleCommanderError(err)) return;
|
|
67
|
+
|
|
68
|
+
// Anything still here is an uncaught throw or an unhandled rejection —
|
|
69
|
+
// a bug rather than a user error, and the class of failure most worth
|
|
70
|
+
// having a record of.
|
|
71
|
+
setOutcome('fatal', {exitCode: 1, error: err});
|
|
72
|
+
|
|
73
|
+
if (inJsonMode()) {
|
|
74
|
+
// Only emit if a command didn't already produce an envelope.
|
|
75
|
+
if (!process.__xdsJsonHandled) {
|
|
76
|
+
process.__xdsJsonHandled = true;
|
|
77
|
+
console.log(JSON.stringify(toErrorEnvelope(err), null, 2));
|
|
78
|
+
}
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
// Human mode: preserve Commander/Node default-ish behavior.
|
|
82
|
+
console.error(
|
|
83
|
+
err instanceof Error ? err.stack || err.message : String(err),
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
process.on('unhandledRejection', handleFatal);
|
|
89
|
+
process.on('uncaughtException', handleFatal);
|
|
90
|
+
|
|
91
|
+
// Pick up the project's `debug` function before parsing, so parse errors
|
|
92
|
+
// and `--help` — which short-circuit before any hook runs — are reported
|
|
93
|
+
// too.
|
|
94
|
+
await loadProjectDebugHandler();
|
|
95
|
+
// Help prints its commands while Commander parses, so a help run reads the
|
|
96
|
+
// project's declared `cliCommand` here.
|
|
97
|
+
await loadProjectForHelp();
|
|
98
|
+
|
|
99
|
+
// `--version --json` answers and exits without Commander, so it runs here —
|
|
100
|
+
// after the handler exists, or the run would go unrecorded.
|
|
101
|
+
handleVersionJsonPreflight();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await program.parseAsync(process.argv);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
handleFatal(err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Find all existing agent doc files in a directory, across EVERY location any
|
|
6
6
|
* preset can write (see {@link AGENT_DOC_PATHS}: AGENTS.md, CLAUDE.md,
|
|
7
|
-
* .claude/CLAUDE.md, .cursorrules, .hermes.md, HERMES.md).
|
|
7
|
+
* .claude/CLAUDE.md, GEMINI.md, .cursorrules, .hermes.md, HERMES.md).
|
|
8
8
|
* @param {string} targetDir
|
|
9
9
|
* @returns {string[]} Relative paths of existing agent doc files
|
|
10
10
|
*/
|
|
11
11
|
export function discoverAgentDocs(targetDir: string): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Whether `content` holds an Astryx block. A managed block that only imports
|
|
14
|
+
* another file (the one-line `@./AGENTS.md` that init writes to GEMINI.md) does
|
|
15
|
+
* not count: it holds nothing once that file has lost its block. A malformed
|
|
16
|
+
* block still counts, so a half-written file reads as set up.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} content
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
export function holdsAstryxBlock(content: string): boolean;
|
|
12
22
|
/**
|
|
13
23
|
* Single source of truth for "is Astryx set up in this project?" — true when any
|
|
14
|
-
* agent-doc file already
|
|
15
|
-
*
|
|
24
|
+
* agent-doc file already holds an Astryx block, i.e. `init` / `agent-docs` has
|
|
25
|
+
* run. Reused by the init & upgrade commands, the per-command setup nudge
|
|
16
26
|
* (enforcement layer 3), and the cli postinstall nudge (layer 2). Core's
|
|
17
27
|
* postinstall (separate package, layer 1) mirrors the same marker contract.
|
|
18
28
|
*
|
|
@@ -68,6 +78,7 @@ export function shouldNudge({ scriptPath, npmCommand, isSetUp }?: {
|
|
|
68
78
|
export const AGENTS_MD: "AGENTS.md";
|
|
69
79
|
export const CLAUDE_MD: "CLAUDE.md";
|
|
70
80
|
export const CLAUDE_DIR_MD: ".claude/CLAUDE.md";
|
|
81
|
+
export const GEMINI_MD: "GEMINI.md";
|
|
71
82
|
export const CURSOR_RULES: ".cursorrules";
|
|
72
83
|
export const HERMES_DOT_MD: ".hermes.md";
|
|
73
84
|
export const HERMES_MD: "HERMES.md";
|
|
@@ -77,10 +88,12 @@ export const LEGACY_MARKER_START: "<!-- XDS:START -->";
|
|
|
77
88
|
export const LEGACY_MARKER_END: "<!-- XDS:END -->";
|
|
78
89
|
/**
|
|
79
90
|
* The canonical set of EVERY location an --agent preset (or the default) can
|
|
80
|
-
* write
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
91
|
+
* write a managed Astryx block — the full block, or the one-line import of
|
|
92
|
+
* AGENTS.md that the default writes to GEMINI.md. SINGLE
|
|
93
|
+
* SOURCE OF TRUTH: discovery, removal, and the `isAstryxInitialized` predicate
|
|
94
|
+
* all derive from this list, so "where init writes" and "where we look" can
|
|
95
|
+
* never drift. (Explicit --agent-docs-path targets are user-chosen and not
|
|
96
|
+
* enumerable here.)
|
|
84
97
|
*/
|
|
85
98
|
export const AGENT_DOC_PATHS: string[];
|
|
86
99
|
/**
|
|
@@ -29,6 +29,7 @@ import * as path from 'node:path';
|
|
|
29
29
|
export const AGENTS_MD = 'AGENTS.md';
|
|
30
30
|
export const CLAUDE_MD = 'CLAUDE.md';
|
|
31
31
|
export const CLAUDE_DIR_MD = '.claude/CLAUDE.md'; // cross-platform literal
|
|
32
|
+
export const GEMINI_MD = 'GEMINI.md';
|
|
32
33
|
export const CURSOR_RULES = '.cursorrules';
|
|
33
34
|
export const HERMES_DOT_MD = '.hermes.md';
|
|
34
35
|
export const HERMES_MD = 'HERMES.md';
|
|
@@ -41,15 +42,18 @@ export const LEGACY_MARKER_END = '<!-- XDS:END -->';
|
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* The canonical set of EVERY location an --agent preset (or the default) can
|
|
44
|
-
* write
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* write a managed Astryx block — the full block, or the one-line import of
|
|
46
|
+
* AGENTS.md that the default writes to GEMINI.md. SINGLE
|
|
47
|
+
* SOURCE OF TRUTH: discovery, removal, and the `isAstryxInitialized` predicate
|
|
48
|
+
* all derive from this list, so "where init writes" and "where we look" can
|
|
49
|
+
* never drift. (Explicit --agent-docs-path targets are user-chosen and not
|
|
50
|
+
* enumerable here.)
|
|
48
51
|
*/
|
|
49
52
|
export const AGENT_DOC_PATHS = [
|
|
50
53
|
AGENTS_MD, // Codex / ChatGPT / Muse / generic
|
|
51
54
|
CLAUDE_MD, // Claude Code (root)
|
|
52
55
|
CLAUDE_DIR_MD, // Claude Code (.claude/CLAUDE.md)
|
|
56
|
+
GEMINI_MD, // Gemini CLI
|
|
53
57
|
CURSOR_RULES, // Cursor
|
|
54
58
|
HERMES_DOT_MD, // Hermes
|
|
55
59
|
HERMES_MD, // Hermes
|
|
@@ -64,7 +68,7 @@ export const INIT_MARKERS = [MARKER_START, LEGACY_MARKER_START];
|
|
|
64
68
|
/**
|
|
65
69
|
* Find all existing agent doc files in a directory, across EVERY location any
|
|
66
70
|
* preset can write (see {@link AGENT_DOC_PATHS}: AGENTS.md, CLAUDE.md,
|
|
67
|
-
* .claude/CLAUDE.md, .cursorrules, .hermes.md, HERMES.md).
|
|
71
|
+
* .claude/CLAUDE.md, GEMINI.md, .cursorrules, .hermes.md, HERMES.md).
|
|
68
72
|
* @param {string} targetDir
|
|
69
73
|
* @returns {string[]} Relative paths of existing agent doc files
|
|
70
74
|
*/
|
|
@@ -72,10 +76,33 @@ export function discoverAgentDocs(targetDir) {
|
|
|
72
76
|
return AGENT_DOC_PATHS.filter(p => fs.existsSync(path.join(targetDir, p)));
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Whether `content` holds an Astryx block. A managed block that only imports
|
|
81
|
+
* another file (the one-line `@./AGENTS.md` that init writes to GEMINI.md) does
|
|
82
|
+
* not count: it holds nothing once that file has lost its block. A malformed
|
|
83
|
+
* block still counts, so a half-written file reads as set up.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} content
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
export function holdsAstryxBlock(content) {
|
|
89
|
+
for (const marker of INIT_MARKERS) {
|
|
90
|
+
let at = content.indexOf(marker);
|
|
91
|
+
while (at !== -1) {
|
|
92
|
+
const from = at + marker.length;
|
|
93
|
+
const end = content.indexOf('<!--', from);
|
|
94
|
+
const inner = (end === -1 ? content.slice(from) : content.slice(from, end)).trim();
|
|
95
|
+
if (!/^@\S+$/.test(inner)) return true;
|
|
96
|
+
at = content.indexOf(marker, from);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
75
102
|
/**
|
|
76
103
|
* Single source of truth for "is Astryx set up in this project?" — true when any
|
|
77
|
-
* agent-doc file already
|
|
78
|
-
*
|
|
104
|
+
* agent-doc file already holds an Astryx block, i.e. `init` / `agent-docs` has
|
|
105
|
+
* run. Reused by the init & upgrade commands, the per-command setup nudge
|
|
79
106
|
* (enforcement layer 3), and the cli postinstall nudge (layer 2). Core's
|
|
80
107
|
* postinstall (separate package, layer 1) mirrors the same marker contract.
|
|
81
108
|
*
|
|
@@ -86,7 +113,7 @@ export function isAstryxInitialized(targetDir = process.cwd()) {
|
|
|
86
113
|
for (const rel of discoverAgentDocs(targetDir)) {
|
|
87
114
|
try {
|
|
88
115
|
const content = fs.readFileSync(path.join(targetDir, rel), 'utf-8');
|
|
89
|
-
if (
|
|
116
|
+
if (holdsAstryxBlock(content)) {
|
|
90
117
|
return true;
|
|
91
118
|
}
|
|
92
119
|
} catch {
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Throw {@link AgentDocsHomeError} when `targetDir` is the home folder, or a
|
|
6
|
+
* settings folder in it outside a project.
|
|
7
|
+
* @param {string} targetDir
|
|
8
|
+
*/
|
|
9
|
+
export function assertAgentDocsTarget(targetDir: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* The folder a run started in `cwd` writes agent docs to: the nearest folder at
|
|
12
|
+
* or above it whose package.json declares an `@astryxdesign` package, else the
|
|
13
|
+
* nearest folder with any package.json. The search stops at a repository root
|
|
14
|
+
* and never reaches the home folder, so a stray `~/package.json` cannot pull
|
|
15
|
+
* the docs there.
|
|
16
|
+
* @param {string} cwd
|
|
17
|
+
* @returns {string | null} null when no folder it searches has a package.json;
|
|
18
|
+
* init then writes where it runs, as before.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveAgentDocsRoot(cwd: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* The folder `init` run in `cwd` writes the agent docs to: the project root
|
|
23
|
+
* {@link resolveAgentDocsRoot} finds, else `cwd` itself. The one definition
|
|
24
|
+
* that init, its removal, and the per-command setup nudge all use.
|
|
25
|
+
* @param {string} cwd
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export function agentDocsTargetDir(cwd: string): string;
|
|
4
29
|
/**
|
|
5
30
|
* Parse the Astryx version a managed block was generated for, from its header
|
|
6
31
|
* line ("Astryx v1.2.3 · N components"). Returns null when the block predates
|
|
@@ -23,6 +48,8 @@ export function parseBlockVersion(content: string): string | null;
|
|
|
23
48
|
* - `current` — every managed block already matches the installed version.
|
|
24
49
|
* And the project is `missing` when no managed block exists anywhere (the repo
|
|
25
50
|
* has agent-doc files without our markers, or none at all — i.e. never `init`ed).
|
|
51
|
+
* A managed import of AGENTS.md is not a block here: it names no version and
|
|
52
|
+
* stays current while AGENTS.md holds the block.
|
|
26
53
|
*
|
|
27
54
|
* @param {string} targetDir
|
|
28
55
|
* @param {string} [installedVersion] Defaults to the installed core version.
|
|
@@ -170,34 +197,70 @@ export function injectAgentsMd(targetDir: string, version: string): void;
|
|
|
170
197
|
*/
|
|
171
198
|
export function injectClaudeMd(targetDir: string, version: string): boolean;
|
|
172
199
|
/**
|
|
173
|
-
* Remove Astryx
|
|
174
|
-
*
|
|
200
|
+
* Remove the Astryx-managed block, between its markers, from a file, leaving
|
|
201
|
+
* every other line as it is. With `deleteIfEmpty`, the file is deleted when
|
|
202
|
+
* nothing is left but whitespace or the exact heading init writes into a file
|
|
203
|
+
* it creates; with `deleteIfBlank`, only when nothing but whitespace is left.
|
|
175
204
|
*
|
|
176
205
|
* @param {string} filePath
|
|
177
|
-
* @param {{deleteIfEmpty?: boolean}} [options]
|
|
206
|
+
* @param {{deleteIfEmpty?: boolean, deleteIfBlank?: boolean}} [options]
|
|
178
207
|
* @returns {boolean} Whether the Astryx section was found and removed
|
|
179
208
|
*/
|
|
180
|
-
export function removeXdsBlock(filePath: string, { deleteIfEmpty }?: {
|
|
209
|
+
export function removeXdsBlock(filePath: string, { deleteIfEmpty, deleteIfBlank }?: {
|
|
181
210
|
deleteIfEmpty?: boolean;
|
|
211
|
+
deleteIfBlank?: boolean;
|
|
182
212
|
}): boolean;
|
|
183
213
|
/**
|
|
184
214
|
* Remove Astryx section from all known agent doc files.
|
|
185
215
|
* @param {string} targetDir
|
|
216
|
+
* @param {object} [options]
|
|
217
|
+
* @param {string} [options.shownFrom] - Folder the logged paths are shown
|
|
218
|
+
* relative to; defaults to `targetDir`.
|
|
219
|
+
* @param {Set<string>} [options.skip] - Files left as they are.
|
|
186
220
|
* @returns {string[]} the project-relative files a block was removed from.
|
|
187
221
|
* @throws {PathSafetyError} `ERR_PATH_TRAVERSAL` when a file it would change
|
|
188
222
|
* resolves outside `targetDir`; nothing is changed.
|
|
189
223
|
*/
|
|
190
|
-
export function removeAgentDocs(targetDir: string
|
|
224
|
+
export function removeAgentDocs(targetDir: string, { shownFrom, skip }?: {
|
|
225
|
+
shownFrom?: string | undefined;
|
|
226
|
+
skip?: Set<string> | undefined;
|
|
227
|
+
}): string[];
|
|
228
|
+
/**
|
|
229
|
+
* Remove the managed block where init writes it, the project root (see
|
|
230
|
+
* {@link resolveAgentDocsRoot}), and in `cwd`, so a block an earlier version
|
|
231
|
+
* wrote in that folder goes too. Each folder is checked before anything is
|
|
232
|
+
* changed. Run below the project root, a project-root agent file holding a
|
|
233
|
+
* block that resolves outside the project is left as it is and reported to
|
|
234
|
+
* `onSkipped`, as install does there; 0.7.x touched only `cwd`. A file in
|
|
235
|
+
* `cwd`, or at the root when run there, that resolves outside refuses the run.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} cwd
|
|
238
|
+
* @param {{onSkipped?: (rel: string, reason: string) => void}} [options]
|
|
239
|
+
* @returns {string[]} the files a block was removed from, relative to `cwd`
|
|
240
|
+
* @throws {PathSafetyError} `ERR_PATH_TRAVERSAL`; nothing is changed
|
|
241
|
+
*/
|
|
242
|
+
export function removeProjectAgentDocs(cwd: string, { onSkipped }?: {
|
|
243
|
+
onSkipped?: (rel: string, reason: string) => void;
|
|
244
|
+
}): string[];
|
|
191
245
|
/**
|
|
192
246
|
* Programmatic entry point for installing agent docs.
|
|
193
247
|
* Used by the init command, upgrade command, and agent-docs command.
|
|
194
248
|
*
|
|
195
|
-
*
|
|
196
|
-
* -
|
|
197
|
-
* -
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* -
|
|
249
|
+
* Default strategy (no agent/paths specified), so every agent reads the block:
|
|
250
|
+
* - Write the block to AGENTS.md (created when missing).
|
|
251
|
+
* - Write the block to the project's Claude Code file: root CLAUDE.md when it
|
|
252
|
+
* exists, else an existing .claude/CLAUDE.md, even when it imports AGENTS.md.
|
|
253
|
+
* Remove a block from the other one. Create no Claude file.
|
|
254
|
+
* - Write a managed import of AGENTS.md to GEMINI.md (created when missing),
|
|
255
|
+
* unless GEMINI.md already imports AGENTS.md.
|
|
256
|
+
* - Keep a full block in every other existing standalone file.
|
|
257
|
+
* - Leave every other `@path` import wrapper untouched; if an older run
|
|
258
|
+
* expanded a block into one, remove that duplicate block. When a wrapper
|
|
259
|
+
* imports a file other than AGENTS.md, the project keeps its own layout:
|
|
260
|
+
* every standalone file gets the block.
|
|
261
|
+
* A refresh (`onlyReplace`) rewrites existing blocks in place, the Claude
|
|
262
|
+
* file's copy included, leaves imports of AGENTS.md as they are, and creates
|
|
263
|
+
* nothing.
|
|
201
264
|
*
|
|
202
265
|
* @param {string} targetDir
|
|
203
266
|
* @param {object} [options]
|
|
@@ -211,11 +274,21 @@ export function removeAgentDocs(targetDir: string): string[];
|
|
|
211
274
|
* so an integration's topics reach the agent.
|
|
212
275
|
* @param {string} [options.renderedBlock] - Fully rendered expected block. Init
|
|
213
276
|
* and upgrade pass one shared block to every target.
|
|
277
|
+
* @param {(rel: string, reason: string) => void} [options.onSkipped] - Called
|
|
278
|
+
* for each file skipped because it resolves outside `targetDir` and 0.7.1
|
|
279
|
+
* never wrote it: GEMINI.md, or any file when `refuseEscapes` is false.
|
|
280
|
+
* @param {boolean} [options.refuseEscapes] - Whether a file 0.7.1 wrote that
|
|
281
|
+
* resolves outside `targetDir` refuses the run (default). Init passes false
|
|
282
|
+
* when it runs below the project root, where 0.7.1 wrote in the folder it
|
|
283
|
+
* ran in instead.
|
|
214
284
|
* @returns {string[]} List of files written
|
|
215
285
|
* @throws {import('../fs/path-safety.mjs').PathSafetyError} when a file it would
|
|
216
286
|
* write resolves outside `targetDir`; nothing is written.
|
|
287
|
+
* @throws {AgentDocsHomeError} when `targetDir` is the home folder, or a settings
|
|
288
|
+
* folder in it outside a project, or, with `paths`, when a file's own folder
|
|
289
|
+
* is; nothing is written.
|
|
217
290
|
*/
|
|
218
|
-
export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, onlyReplace, topics, renderedBlock, }?: {
|
|
291
|
+
export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, onlyReplace, topics, renderedBlock, onSkipped, refuseEscapes, }?: {
|
|
219
292
|
zh?: boolean | undefined;
|
|
220
293
|
lang?: string | undefined;
|
|
221
294
|
agent?: string | undefined;
|
|
@@ -223,7 +296,22 @@ export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, on
|
|
|
223
296
|
onlyReplace?: boolean | undefined;
|
|
224
297
|
topics?: string[] | undefined;
|
|
225
298
|
renderedBlock?: string | undefined;
|
|
299
|
+
onSkipped?: ((rel: string, reason: string) => void) | undefined;
|
|
300
|
+
refuseEscapes?: boolean | undefined;
|
|
226
301
|
}): string[];
|
|
302
|
+
/**
|
|
303
|
+
* Thrown when agent docs would be written into the home folder, or into a
|
|
304
|
+
* settings folder in it such as `~/.gemini` outside a project. Agents read the
|
|
305
|
+
* files there (for example `~/.claude/CLAUDE.md` or `~/.gemini/GEMINI.md`) in
|
|
306
|
+
* every project, so a project's version-pinned block never goes there. Removal
|
|
307
|
+
* stays allowed.
|
|
308
|
+
*/
|
|
309
|
+
export class AgentDocsHomeError extends Error {
|
|
310
|
+
/** @param {string} dir */
|
|
311
|
+
constructor(dir: string);
|
|
312
|
+
/** The refused folder. */
|
|
313
|
+
dir: string;
|
|
314
|
+
}
|
|
227
315
|
import { discoverAgentDocs } from './agent-doc-state.mjs';
|
|
228
316
|
import { isAstryxInitialized } from './agent-doc-state.mjs';
|
|
229
317
|
import { isAstryxPromptInReach } from './agent-doc-state.mjs';
|