@aligndottech/cli 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -10
- package/dist/cli.d.ts +28 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +98 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +44 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/connect-agents.d.ts +1 -0
- package/dist/commands/connect-agents.d.ts.map +1 -1
- package/dist/commands/connect-agents.js +10 -6
- package/dist/commands/connect-agents.js.map +1 -1
- package/dist/commands/connect.d.ts +31 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +92 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +52 -10
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/decisions/index.d.ts.map +1 -1
- package/dist/commands/decisions/index.js +5 -0
- package/dist/commands/decisions/index.js.map +1 -1
- package/dist/commands/default-action.d.ts +3 -1
- package/dist/commands/default-action.d.ts.map +1 -1
- package/dist/commands/default-action.js +29 -27
- package/dist/commands/default-action.js.map +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +3 -0
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import/sessions.d.ts +10 -2
- package/dist/commands/import/sessions.d.ts.map +1 -1
- package/dist/commands/import/sessions.js +98 -3
- package/dist/commands/import/sessions.js.map +1 -1
- package/dist/commands/import.d.ts +7 -0
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +42 -2
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/invite.d.ts +25 -0
- package/dist/commands/invite.d.ts.map +1 -0
- package/dist/commands/invite.js +68 -0
- package/dist/commands/invite.js.map +1 -0
- package/dist/commands/links.d.ts.map +1 -1
- package/dist/commands/links.js +5 -0
- package/dist/commands/links.js.map +1 -1
- package/dist/commands/mcp.d.ts +64 -35
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +85 -40
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/ratify.d.ts.map +1 -1
- package/dist/commands/ratify.js +21 -0
- package/dist/commands/ratify.js.map +1 -1
- package/dist/commands/registry.d.ts +30 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +70 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/setup.d.ts +45 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +197 -88
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +10 -22
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/telemetry.d.ts +7 -3
- package/dist/commands/telemetry.d.ts.map +1 -1
- package/dist/commands/telemetry.js +12 -7
- package/dist/commands/telemetry.js.map +1 -1
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/commands/why.js +54 -12
- package/dist/commands/why.js.map +1 -1
- package/dist/index.js +4 -90
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-rules.d.ts +1 -0
- package/dist/lib/agent-rules.d.ts.map +1 -1
- package/dist/lib/agent-rules.js +60 -0
- package/dist/lib/agent-rules.js.map +1 -1
- package/dist/lib/capture-report.d.ts +4 -0
- package/dist/lib/capture-report.d.ts.map +1 -1
- package/dist/lib/capture-report.js +18 -1
- package/dist/lib/capture-report.js.map +1 -1
- package/dist/lib/commands-doc.d.ts +15 -0
- package/dist/lib/commands-doc.d.ts.map +1 -0
- package/dist/lib/commands-doc.js +85 -0
- package/dist/lib/commands-doc.js.map +1 -0
- package/dist/lib/config.d.ts +14 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +7 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/connect-prompt.d.ts +1 -1
- package/dist/lib/connect-prompt.js +2 -2
- package/dist/lib/connect-prompt.js.map +1 -1
- package/dist/lib/decision-links.d.ts +39 -0
- package/dist/lib/decision-links.d.ts.map +1 -1
- package/dist/lib/decision-links.js +51 -0
- package/dist/lib/decision-links.js.map +1 -1
- package/dist/lib/fetchers/linear.d.ts.map +1 -1
- package/dist/lib/fetchers/linear.js +27 -1
- package/dist/lib/fetchers/linear.js.map +1 -1
- package/dist/lib/fetchers/slack.d.ts +12 -1
- package/dist/lib/fetchers/slack.d.ts.map +1 -1
- package/dist/lib/fetchers/slack.js +12 -1
- package/dist/lib/fetchers/slack.js.map +1 -1
- package/dist/lib/first-decision.d.ts +22 -0
- package/dist/lib/first-decision.d.ts.map +1 -0
- package/dist/lib/first-decision.js +22 -0
- package/dist/lib/first-decision.js.map +1 -0
- package/dist/lib/gateway-client.d.ts +13 -0
- package/dist/lib/gateway-client.d.ts.map +1 -1
- package/dist/lib/gateway-client.js +38 -0
- package/dist/lib/gateway-client.js.map +1 -1
- package/dist/lib/git.d.ts +28 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +42 -0
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/hook-context.d.ts +7 -0
- package/dist/lib/hook-context.d.ts.map +1 -0
- package/dist/lib/hook-context.js +37 -0
- package/dist/lib/hook-context.js.map +1 -0
- package/dist/lib/hook-payload.d.ts +7 -0
- package/dist/lib/hook-payload.d.ts.map +1 -1
- package/dist/lib/hook-payload.js +56 -8
- package/dist/lib/hook-payload.js.map +1 -1
- package/dist/lib/invite-prompt.d.ts +46 -0
- package/dist/lib/invite-prompt.d.ts.map +1 -0
- package/dist/lib/invite-prompt.js +63 -0
- package/dist/lib/invite-prompt.js.map +1 -0
- package/dist/lib/local-db.d.ts +32 -2
- package/dist/lib/local-db.d.ts.map +1 -1
- package/dist/lib/local-db.js +80 -5
- package/dist/lib/local-db.js.map +1 -1
- package/dist/lib/local-embeddings.d.ts +14 -0
- package/dist/lib/local-embeddings.d.ts.map +1 -1
- package/dist/lib/local-embeddings.js +16 -2
- package/dist/lib/local-embeddings.js.map +1 -1
- package/dist/lib/local-gateway-client.d.ts.map +1 -1
- package/dist/lib/local-gateway-client.js +23 -3
- package/dist/lib/local-gateway-client.js.map +1 -1
- package/dist/lib/local-llm.d.ts +60 -5
- package/dist/lib/local-llm.d.ts.map +1 -1
- package/dist/lib/local-llm.js +92 -10
- package/dist/lib/local-llm.js.map +1 -1
- package/dist/lib/mcp-instructions.shared.d.ts +40 -0
- package/dist/lib/mcp-instructions.shared.d.ts.map +1 -0
- package/dist/lib/mcp-instructions.shared.js +56 -0
- package/dist/lib/mcp-instructions.shared.js.map +1 -0
- package/dist/lib/mcp-setup.d.ts +32 -2
- package/dist/lib/mcp-setup.d.ts.map +1 -1
- package/dist/lib/mcp-setup.js +92 -6
- package/dist/lib/mcp-setup.js.map +1 -1
- package/dist/lib/next-step.d.ts +92 -0
- package/dist/lib/next-step.d.ts.map +1 -0
- package/dist/lib/next-step.js +97 -0
- package/dist/lib/next-step.js.map +1 -0
- package/dist/lib/personal-email-domains.d.ts +8 -0
- package/dist/lib/personal-email-domains.d.ts.map +1 -0
- package/dist/lib/personal-email-domains.js +39 -0
- package/dist/lib/personal-email-domains.js.map +1 -0
- package/dist/lib/personal-import.d.ts +2 -0
- package/dist/lib/personal-import.d.ts.map +1 -1
- package/dist/lib/personal-import.js +4 -1
- package/dist/lib/personal-import.js.map +1 -1
- package/dist/lib/read-value-rollup.d.ts +18 -0
- package/dist/lib/read-value-rollup.d.ts.map +1 -0
- package/dist/lib/read-value-rollup.js +33 -0
- package/dist/lib/read-value-rollup.js.map +1 -0
- package/dist/lib/sessions/import-summary.d.ts +39 -0
- package/dist/lib/sessions/import-summary.d.ts.map +1 -0
- package/dist/lib/sessions/import-summary.js +41 -0
- package/dist/lib/sessions/import-summary.js.map +1 -0
- package/dist/lib/sessions/memory-files.d.ts +85 -0
- package/dist/lib/sessions/memory-files.d.ts.map +1 -0
- package/dist/lib/sessions/memory-files.js +235 -0
- package/dist/lib/sessions/memory-files.js.map +1 -0
- package/dist/lib/sessions/source-url.d.ts +11 -0
- package/dist/lib/sessions/source-url.d.ts.map +1 -1
- package/dist/lib/sessions/source-url.js +15 -0
- package/dist/lib/sessions/source-url.js.map +1 -1
- package/dist/lib/setup-funnel.d.ts +41 -0
- package/dist/lib/setup-funnel.d.ts.map +1 -0
- package/dist/lib/setup-funnel.js +28 -0
- package/dist/lib/setup-funnel.js.map +1 -0
- package/dist/lib/telemetry-consent.d.ts +17 -8
- package/dist/lib/telemetry-consent.d.ts.map +1 -1
- package/dist/lib/telemetry-consent.js +21 -6
- package/dist/lib/telemetry-consent.js.map +1 -1
- package/dist/lib/telemetry-env.d.ts +19 -0
- package/dist/lib/telemetry-env.d.ts.map +1 -0
- package/dist/lib/telemetry-env.js +13 -0
- package/dist/lib/telemetry-env.js.map +1 -0
- package/dist/lib/usage-telemetry.d.ts +108 -13
- package/dist/lib/usage-telemetry.d.ts.map +1 -1
- package/dist/lib/usage-telemetry.js +227 -55
- package/dist/lib/usage-telemetry.js.map +1 -1
- package/dist/lib/user-hooks.d.ts +15 -0
- package/dist/lib/user-hooks.d.ts.map +1 -0
- package/dist/lib/user-hooks.js +144 -0
- package/dist/lib/user-hooks.js.map +1 -0
- package/dist/lib/value-rollup.d.ts +10 -3
- package/dist/lib/value-rollup.d.ts.map +1 -1
- package/dist/lib/value-rollup.js +11 -4
- package/dist/lib/value-rollup.js.map +1 -1
- package/package.json +2 -1
package/dist/commands/setup.js
CHANGED
|
@@ -29,6 +29,9 @@ const { version } = pkg;
|
|
|
29
29
|
import { printBanner } from '../lib/brand.js';
|
|
30
30
|
import { guardedPrompt } from '../lib/prompt-guard.js';
|
|
31
31
|
import { setupSummaryLine, unresolvedGaps } from '../lib/connect-prompt.js';
|
|
32
|
+
import { createSetupFunnel } from '../lib/setup-funnel.js';
|
|
33
|
+
import { agentAskLine, agentConnectedLine, orderAgents, projectAgentsFromWritten } from '../lib/next-step.js';
|
|
34
|
+
import { firstDecision } from '../lib/first-decision.js';
|
|
32
35
|
const TIER_ORDER = { personal: 0, site: 1, workspace: 2 };
|
|
33
36
|
// How many connectors import concurrently after auth. Each import is itself
|
|
34
37
|
// batch-parallel (runPersonalImport), so this bounds total gateway load.
|
|
@@ -334,23 +337,30 @@ async function collectTokens(source, seed = {}, opts = {}) {
|
|
|
334
337
|
* unmarked default, everything else is explicit.
|
|
335
338
|
*/
|
|
336
339
|
export function importRetryHint(sourceId, envName) {
|
|
337
|
-
return envName === 'prod' ? `align
|
|
340
|
+
return envName === 'prod' ? `align connect ${sourceId}` : `align connect ${sourceId} --env ${envName}`;
|
|
338
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Returns the repo-relative files written (empty when the write failed), so the outro can
|
|
344
|
+
* tell whether the project .mcp.json - the one that wires Claude Code - actually landed
|
|
345
|
+
* (ALI-950). Naming an agent as connected off a write that threw would be false.
|
|
346
|
+
*/
|
|
339
347
|
function writeAgentAlignment(envName) {
|
|
340
348
|
try {
|
|
341
349
|
const written = setupAgentAlignment({ cwd: process.cwd(), env: envName });
|
|
342
350
|
p.log.success(`Auto-alignment configured: ${written.join(', ')}`);
|
|
343
351
|
p.log.info(chalk.dim(' A PostToolUse hook will check edits against your decision graph. Claude Code asks ' +
|
|
344
352
|
'once to approve project hooks - accept it to enable automatic alignment.'));
|
|
353
|
+
return written;
|
|
345
354
|
}
|
|
346
355
|
catch (err) {
|
|
347
356
|
p.log.warn(`Could not write auto-alignment files: ${err.message}`);
|
|
357
|
+
return [];
|
|
348
358
|
}
|
|
349
359
|
}
|
|
350
360
|
/** Thrown inside the docs block to leave it without starting a read; never surfaces. */
|
|
351
361
|
class SkipDocs extends Error {
|
|
352
362
|
}
|
|
353
|
-
async function runLocalValuePhase(opts
|
|
363
|
+
async function runLocalValuePhase(opts) {
|
|
354
364
|
// Without a TTY neither prompt below can work: a piped stdin hangs forever and a closed
|
|
355
365
|
// stdin crashes clack's raw-mode init (uv_tty_init EINVAL) AFTER local setup has already
|
|
356
366
|
// succeeded (align-cli#118). Computed once, up front, and reused by both prompts in this
|
|
@@ -362,6 +372,10 @@ async function runLocalValuePhase(opts = {}) {
|
|
|
362
372
|
const localEnv = config.getEnvironment('local');
|
|
363
373
|
const localClient = createGatewayClient(localEnv);
|
|
364
374
|
const capture = createCaptureCollector();
|
|
375
|
+
// ALI-949: local setup has begun. Sends now on a re-run (consent already on disk); on a
|
|
376
|
+
// first run it cannot, and the post-consent offer below is the one that lands. Fire-and-
|
|
377
|
+
// forget: a slow gateway never delays the value moment.
|
|
378
|
+
void opts.funnel.started(localEnv);
|
|
365
379
|
// ALI-794: value before questions. Git needs no credential and no consent, so it runs
|
|
366
380
|
// before anything is asked - the found-decisions summary below is what "here is what I
|
|
367
381
|
// found" means, and it has to exist before the agent-wiring/consent/connector steps that
|
|
@@ -388,7 +402,7 @@ async function runLocalValuePhase(opts = {}) {
|
|
|
388
402
|
repoKnown = gitCount > 0;
|
|
389
403
|
if (repoKnown) {
|
|
390
404
|
console.log('');
|
|
391
|
-
p.log.info(chalk.dim(`Git: ${gitCount} decisions from ${repo} are already in your graph. \`align
|
|
405
|
+
p.log.info(chalk.dim(`Git: ${gitCount} decisions from ${repo} are already in your graph. \`align connect git --env local\` refreshes them.`));
|
|
392
406
|
}
|
|
393
407
|
docsKnown = knownDb.hasDocsForRepo(repo);
|
|
394
408
|
}
|
|
@@ -465,7 +479,7 @@ async function runLocalValuePhase(opts = {}) {
|
|
|
465
479
|
// not after them.
|
|
466
480
|
console.log('');
|
|
467
481
|
if (docsKnown) {
|
|
468
|
-
p.log.info(chalk.dim('Repo docs: already in your graph. `align
|
|
482
|
+
p.log.info(chalk.dim('Repo docs: already in your graph. `align connect docs --env local` refreshes them.'));
|
|
469
483
|
}
|
|
470
484
|
const localDocsSpinner = p.spinner();
|
|
471
485
|
if (!docsKnown)
|
|
@@ -485,6 +499,18 @@ async function runLocalValuePhase(opts = {}) {
|
|
|
485
499
|
local: true,
|
|
486
500
|
funnel: { env: localEnv, source: 'docs' },
|
|
487
501
|
});
|
|
502
|
+
// ALI-950: a repo can carry decision-shaped docs and no git history worth mining, and
|
|
503
|
+
// the outro's question must name a decision the wizard FOUND, whichever source found
|
|
504
|
+
// it. Read back the same way the git summary does, only when git left nothing to name.
|
|
505
|
+
if (!firstFoundTitle) {
|
|
506
|
+
const docsDb = createLocalDb(dbPath);
|
|
507
|
+
try {
|
|
508
|
+
firstFoundTitle = buildFoundSummary(docsDb).recent[0]?.title;
|
|
509
|
+
}
|
|
510
|
+
finally {
|
|
511
|
+
docsDb.close();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
488
514
|
}
|
|
489
515
|
else {
|
|
490
516
|
localDocsSpinner.stop('No ADRs or CLAUDE.md/AGENTS.md content found');
|
|
@@ -494,36 +520,51 @@ async function runLocalValuePhase(opts = {}) {
|
|
|
494
520
|
if (!(e instanceof SkipDocs))
|
|
495
521
|
localDocsSpinner.stop(`Docs import skipped - ${e.message}`);
|
|
496
522
|
}
|
|
497
|
-
writeAgentAlignment('local');
|
|
523
|
+
const projectAgents = projectAgentsFromWritten(writeAgentAlignment('local'));
|
|
498
524
|
// The agents installed on this machine, not just the ones this project configures. Local
|
|
499
525
|
// setup skipped this and cloud did not, which is backwards: local mode is the one whose
|
|
500
526
|
// entire pitch is an agent on your own machine reading a graph that never leaves it.
|
|
501
527
|
console.log('');
|
|
502
528
|
const localAgents = await connectDetectedAgents('local');
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
|
|
529
|
+
// ALI-950: whenever ANY agent was wired - the project .mcp.json counts. This used to print
|
|
530
|
+
// only when a GLOBAL config was written, so a Claude-Code-only user, wired through the
|
|
531
|
+
// project file a few lines up, never saw it. The question itself moves to the outro's
|
|
532
|
+
// last line, where it is the one thing left to do.
|
|
533
|
+
const agents = orderAgents({ project: projectAgents, global: localAgents.wired });
|
|
534
|
+
const connectedLine = agentConnectedLine(agents);
|
|
535
|
+
if (connectedLine) {
|
|
507
536
|
console.log('');
|
|
508
|
-
p.log.info(chalk.dim(
|
|
509
|
-
p.log.info(chalk.bold(firstFoundTitle ? ` "why did we ${firstFoundTitle}?"` : ' "What decisions exist in this codebase?"'));
|
|
537
|
+
p.log.info(chalk.dim(connectedLine));
|
|
510
538
|
}
|
|
511
539
|
// ALI-618: one-time, never asked again once answered. Local mode has no account, so consent
|
|
512
540
|
// is the only thing that can ever turn this on - see usage-telemetry.ts's local-embedded
|
|
513
541
|
// branch, which reads this same stored decision.
|
|
514
542
|
await maybeRequestTelemetryConsent(config, Boolean(interactive));
|
|
515
|
-
|
|
543
|
+
// ALI-949: the first checkpoint at which a local-mode setup_started CAN send - consent is
|
|
544
|
+
// only now on disk (or was already, on a re-run). The wizard-start offer in runSetup could
|
|
545
|
+
// not send for a fresh install, so the funnel object is offered the local env again here.
|
|
546
|
+
void opts.funnel.started(localEnv);
|
|
547
|
+
return { interactive, config, localEnv, localClient, dbPath, opts, capture, funnel: opts.funnel, agents, firstFoundTitle };
|
|
548
|
+
}
|
|
549
|
+
/** The ids `align connect --source` accepts: every local paste-token source, in picker order. */
|
|
550
|
+
export function localConnectorIds() {
|
|
551
|
+
return buildSources(false)
|
|
552
|
+
.filter((s) => s.id !== 'git' && s.tokenLabel)
|
|
553
|
+
.sort((a, b) => TIER_ORDER[a.tier ?? 'personal'] - TIER_ORDER[b.tier ?? 'personal'])
|
|
554
|
+
.map((s) => s.id);
|
|
516
555
|
}
|
|
517
556
|
/**
|
|
518
|
-
* The
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
* the
|
|
523
|
-
*
|
|
557
|
+
* The picker-and-import tail of local setup: ask which read-only-token sources to connect,
|
|
558
|
+
* collect every credential up front, then fetch and import each one. Extracted from
|
|
559
|
+
* runLocalConnectorPhase for `align connect` (ALI-951), which is this same flow without the
|
|
560
|
+
* wizard around it - one picker, one paste path, one save rule, in one place. The body is
|
|
561
|
+
* the wizard's; `preselected`, `seedTokens` and `json` are the only additions, and each is
|
|
562
|
+
* inert when absent.
|
|
524
563
|
*/
|
|
525
|
-
async function
|
|
526
|
-
const { interactive, config, localEnv, localClient,
|
|
564
|
+
export async function connectLocalSources(o) {
|
|
565
|
+
const { interactive, config, localEnv, localClient, capture, approve } = o;
|
|
566
|
+
const quiet = o.json === true;
|
|
567
|
+
const results = [];
|
|
527
568
|
// Connectors: local mode connects by a read-only token the user mints themselves,
|
|
528
569
|
// for every connector - their personal graph, their credential. OAuth belongs to
|
|
529
570
|
// the personal-cloud path, where the hosted broker holds the client secrets. (This
|
|
@@ -538,13 +579,21 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
538
579
|
const localConnectors = buildSources(false)
|
|
539
580
|
.filter((s) => s.id !== 'git' && s.tokenLabel)
|
|
540
581
|
.sort((a, b) => TIER_ORDER[a.tier ?? 'personal'] - TIER_ORDER[b.tier ?? 'personal']);
|
|
541
|
-
|
|
582
|
+
if (o.preselected) {
|
|
583
|
+
const known = new Set(localConnectors.map((s) => s.id));
|
|
584
|
+
const unknown = o.preselected.filter((id) => !known.has(id));
|
|
585
|
+
if (unknown.length) {
|
|
586
|
+
throw new Error(`Unknown source ${unknown.join(', ')}. --source takes one of: ${[...known].join(', ')}.`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (!quiet)
|
|
590
|
+
console.log('');
|
|
542
591
|
// Say WHY, at the point of use. This reason used to live only in the comment above:
|
|
543
592
|
// the user was sent to a provider page to mint a token with no explanation, which reads
|
|
544
593
|
// as the tool being clumsy rather than as the privacy trade they chose. The constraint
|
|
545
594
|
// is the provider's, not ours - OAuth needs a client secret, and a secret inside a
|
|
546
595
|
// distributed binary is not a secret. See ALI-778.
|
|
547
|
-
if (interactive && localConnectors.length > 0) {
|
|
596
|
+
if (interactive && localConnectors.length > 0 && !quiet) {
|
|
548
597
|
// ONE story, stated as the design it is (Tom, 2026-08-31, superseding ALI-778's
|
|
549
598
|
// local-OAuth direction): this is the user's PERSONAL graph, so the credential is
|
|
550
599
|
// one they mint, scope and can revoke themselves. Earlier versions blamed the
|
|
@@ -564,7 +613,7 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
564
613
|
if (saved?.['token'])
|
|
565
614
|
savedTokens.set(source.id, saved);
|
|
566
615
|
}
|
|
567
|
-
if (savedTokens.size > 0) {
|
|
616
|
+
if (savedTokens.size > 0 && !quiet && !o.preselected) {
|
|
568
617
|
const connected = localConnectors.filter((s) => savedTokens.has(s.id));
|
|
569
618
|
const names = connected.map((s) => s.label).join(', ');
|
|
570
619
|
// Named out loud, and left alone: a re-run adds what you pick and touches nothing else.
|
|
@@ -575,35 +624,38 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
575
624
|
p.log.info(chalk.dim(`Connected: ${names} (saved read-only tokens). Select one to re-import it, or to replace its token.`));
|
|
576
625
|
}
|
|
577
626
|
else {
|
|
578
|
-
const refresh = connected.map((s) => `align
|
|
627
|
+
const refresh = connected.map((s) => `align connect --source ${s.id} --yes`).join(', ');
|
|
579
628
|
p.log.info(chalk.dim(`Connected: ${names} (saved read-only tokens), left alone: no terminal to pick from. Refresh with ${refresh}.`));
|
|
580
629
|
}
|
|
581
630
|
}
|
|
582
|
-
// `interactive` computed once,
|
|
631
|
+
// `interactive` computed once, by the caller - see runLocalValuePhase.
|
|
583
632
|
// ALI-794: the found-summary above sits between this picker and the last clean screen,
|
|
584
633
|
// which is the exact condition that used to corrupt clack's in-place redraw for an
|
|
585
634
|
// outside tester (2026-08-30). Clear first so the picker gets its own canvas.
|
|
586
|
-
|
|
635
|
+
const askPicker = interactive && !o.preselected;
|
|
636
|
+
if (askPicker)
|
|
587
637
|
clearScreenForPicker();
|
|
588
638
|
// A windowed list ends in "..." and reads as cut off; the count turns it into "scroll".
|
|
589
639
|
const maxItems = pickerMaxItems(process.stdout.rows, localConnectors.length);
|
|
590
640
|
const windowed = maxItems < localConnectors.length;
|
|
591
|
-
const selected =
|
|
592
|
-
?
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
641
|
+
const selected = o.preselected
|
|
642
|
+
? o.preselected
|
|
643
|
+
: askPicker
|
|
644
|
+
? await p.multiselect({
|
|
645
|
+
message: `Connect more sources with a read-only token? (skip to finish${windowed ? `; ${localConnectors.length} sources, scroll for more` : ''})`,
|
|
646
|
+
options: localConnectors.map((s) => ({
|
|
647
|
+
value: s.id,
|
|
648
|
+
label: s.label,
|
|
649
|
+
// A connected connector stays in the list so it can be re-imported, or its expired
|
|
650
|
+
// token replaced, without a separate command. Not selecting it leaves it alone.
|
|
651
|
+
hint: savedTokens.has(s.id) ? 'connected - select to re-import' : s.description,
|
|
652
|
+
})),
|
|
653
|
+
required: false,
|
|
654
|
+
// Without maxItems clack renders all eight and its in-place redraw miscounts
|
|
655
|
+
// once the list is taller than the viewport, painting duplicate rows.
|
|
656
|
+
maxItems,
|
|
657
|
+
})
|
|
658
|
+
: [];
|
|
607
659
|
// Collect every credential up front, so the automatic phase below never stops to ask.
|
|
608
660
|
const localReady = [];
|
|
609
661
|
if (!p.isCancel(selected)) {
|
|
@@ -612,17 +664,23 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
612
664
|
const source = localConnectors.find((s) => s.id === id);
|
|
613
665
|
if (!source)
|
|
614
666
|
continue;
|
|
615
|
-
|
|
616
|
-
|
|
667
|
+
if (!quiet) {
|
|
668
|
+
console.log('');
|
|
669
|
+
p.log.step(chalk.bold(source.label));
|
|
670
|
+
}
|
|
617
671
|
// Jira and Confluence share one Atlassian account: same email, same site
|
|
618
672
|
// domain, same id.atlassian.com API token. Ask once, reuse for the other, and
|
|
619
673
|
// SAY so - the same disclosure rule as the gh-token reuse, because a silently
|
|
620
674
|
// absorbed credential is the thing nobody can audit afterwards.
|
|
621
675
|
const isAtlassian = source.id === 'jira' || source.id === 'confluence';
|
|
622
676
|
let seed = isAtlassian ? { ...atlassianShared } : {};
|
|
623
|
-
if (isAtlassian && seed['token'] !== undefined) {
|
|
677
|
+
if (isAtlassian && seed['token'] !== undefined && !quiet) {
|
|
624
678
|
p.log.info(chalk.dim(' Using your Atlassian email, domain and API token from the previous connector.'));
|
|
625
679
|
}
|
|
680
|
+
// ALI-951: `--token` (and any other field a flag supplied) seeds every preselected
|
|
681
|
+
// source the way the Atlassian reuse above does - a seeded field is never asked for.
|
|
682
|
+
if (o.seedTokens)
|
|
683
|
+
seed = { ...seed, ...o.seedTokens };
|
|
626
684
|
// A connected connector that was selected: re-import with the saved token unless the
|
|
627
685
|
// user wants to replace it. Seeding collectTokens with the saved fields is what skips
|
|
628
686
|
// every paste; an empty seed is what asks for them. --approve never stops to ask.
|
|
@@ -633,7 +691,7 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
633
691
|
const saved = savedTokens.get(source.id);
|
|
634
692
|
let reused = false;
|
|
635
693
|
if (saved && seed['token'] === undefined) {
|
|
636
|
-
const reuse =
|
|
694
|
+
const reuse = approve
|
|
637
695
|
? true
|
|
638
696
|
: await p.confirm({
|
|
639
697
|
message: source.tokenShortLived
|
|
@@ -648,7 +706,19 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
648
706
|
reused = true;
|
|
649
707
|
}
|
|
650
708
|
}
|
|
651
|
-
|
|
709
|
+
// ALI-951: with no terminal there is nothing to paste into. Name the flag rather than
|
|
710
|
+
// letting clack crash on a closed stdin, which reads as the tool breaking (align-cli#118).
|
|
711
|
+
if (!interactive) {
|
|
712
|
+
const missing = (source.extraFields ?? []).map((f) => f.key).filter((k) => seed[k] === undefined);
|
|
713
|
+
if (source.tokenLabel && seed['token'] === undefined)
|
|
714
|
+
missing.push('token');
|
|
715
|
+
if (missing.length) {
|
|
716
|
+
const fieldFlags = missing.filter((k) => k !== 'token').map((k) => `--${k} <${k}>`);
|
|
717
|
+
throw new Error(`${source.label} needs a ${source.tokenLabel ?? 'token'} and there is no terminal to paste one into. ` +
|
|
718
|
+
`Pass --token <token>${fieldFlags.length ? `, or run align connect ${source.id} ${fieldFlags.join(' ')} --token <token>` : ''}.`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const tokens = await collectTokens(source, seed, { approve });
|
|
652
722
|
if (!tokens)
|
|
653
723
|
continue;
|
|
654
724
|
if (isAtlassian) {
|
|
@@ -665,7 +735,7 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
665
735
|
// found" moment, not stranded behind the questions this phase exists to ask. This phase
|
|
666
736
|
// is only the automatic import for the paste-token connectors just collected.
|
|
667
737
|
for (const { source, tokens, reused } of localReady) {
|
|
668
|
-
const spinner = p.spinner();
|
|
738
|
+
const spinner = quiet ? { start() { }, stop() { } } : p.spinner();
|
|
669
739
|
spinner.start(`Fetching from ${source.label}...`);
|
|
670
740
|
try {
|
|
671
741
|
const fetched = await source.fetch(tokens);
|
|
@@ -677,15 +747,19 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
677
747
|
// there is one rule here rather than a branch that has to stay in step with the reuse.
|
|
678
748
|
config.saveConnectorFields('local', source.id, tokens);
|
|
679
749
|
spinner.stop(`Found ${items.length} items`);
|
|
750
|
+
let imported = 0;
|
|
680
751
|
if (items.length) {
|
|
681
|
-
await runPersonalImport(items, localClient, {
|
|
752
|
+
imported = await runPersonalImport(items, localClient, {
|
|
682
753
|
label: source.label,
|
|
683
754
|
approve: true,
|
|
684
755
|
appUrl: resolveAppUrl(localEnv),
|
|
685
756
|
local: true,
|
|
757
|
+
quiet: quiet || undefined,
|
|
758
|
+
silent: quiet || undefined,
|
|
686
759
|
funnel: { env: localEnv, source: source.id },
|
|
687
760
|
});
|
|
688
761
|
}
|
|
762
|
+
results.push({ id: source.id, label: source.label, found: items.length, imported });
|
|
689
763
|
}
|
|
690
764
|
catch (e) {
|
|
691
765
|
const msg = e.message;
|
|
@@ -700,8 +774,14 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
700
774
|
else {
|
|
701
775
|
spinner.stop(`Skipped ${source.label} - ${msg}`);
|
|
702
776
|
}
|
|
777
|
+
results.push({ id: source.id, label: source.label, found: 0, imported: 0, error: msg });
|
|
703
778
|
}
|
|
704
779
|
}
|
|
780
|
+
return results;
|
|
781
|
+
}
|
|
782
|
+
async function runLocalConnectorPhase(ctx) {
|
|
783
|
+
const { interactive, config, localEnv, localClient, dbPath, opts, capture, funnel, agents, firstFoundTitle } = ctx;
|
|
784
|
+
await connectLocalSources({ interactive, config, localEnv, localClient, capture, approve: opts.approve ?? false });
|
|
705
785
|
// ALI-827: what each source fetched and what it could not reach - once, after every
|
|
706
786
|
// import has printed its own line, so the numbers sit together. A source whose fetch
|
|
707
787
|
// THREW is not in it: its spinner line above already named the error, and a row of
|
|
@@ -733,7 +813,7 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
733
813
|
? `export ${chalk.bold('ALIGN_ENV=local')} (or unset it)`
|
|
734
814
|
: `run ${chalk.bold('align env set local')} to make local the default ` +
|
|
735
815
|
`(${chalk.dim(`align env set ${bareEnv}`)} switches back)`;
|
|
736
|
-
p.log.warn(`Bare commands (align ask, align
|
|
816
|
+
p.log.warn(`Bare commands (align ask, align connect ...) use the ${bareEnv} cloud graph, not this local one, because ${cause}.\n` +
|
|
737
817
|
`Add ${chalk.bold('--env local')} per command, or ${remedy}.`);
|
|
738
818
|
}
|
|
739
819
|
// ALI-796: the graph names its own gaps - a ref whose platform has no connected
|
|
@@ -744,19 +824,24 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
744
824
|
const gaps = unresolvedGaps(refsDb.getAllRefs(), isConnected);
|
|
745
825
|
refsDb.close();
|
|
746
826
|
const gapLine = setupSummaryLine(gaps);
|
|
827
|
+
// ALI-950: the last line is the next step, and it happens in the agent - named, with a
|
|
828
|
+
// question about a decision the wizard just found, and no CLI verb. This used to end on
|
|
829
|
+
// `align ask "why <a thing you decided>"`, a prompt verb on the one screen whose job is to
|
|
830
|
+
// send the user into their agent. The gap line, when there is one, sits above it.
|
|
747
831
|
p.outro(`${chalk.green('You are set up in local mode.')}\n` +
|
|
748
832
|
` Graph: ${chalk.dim(dbPath)}\n` +
|
|
749
|
-
` Run ${chalk.bold('align')} any time to see your graph and what to do next
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
833
|
+
` Run ${chalk.bold('align')} any time to see your graph and what to do next.` +
|
|
834
|
+
`${gapLine ? `\n\n ${chalk.dim(gapLine)}` : ''}\n\n` +
|
|
835
|
+
` ${chalk.bold(agentAskLine({ agents, firstTitle: firstFoundTitle, inRepo: true, envName: 'local' }))}`);
|
|
836
|
+
// ALI-949: after the outro, never before it - telemetry must not delay what the user is
|
|
837
|
+
// waiting for. Awaited (unlike the other emitters) because this is the wizard's last act
|
|
838
|
+
// and the one row the funnel was blind to; there is nothing left for a 2s worst case to hold up.
|
|
839
|
+
await funnel.completed(localEnv);
|
|
755
840
|
}
|
|
756
841
|
// Local-embedded onboarding (opt-in via --local): no account, no cloud, no OAuth. Composes
|
|
757
842
|
// the two phases above unchanged - this is exactly what ran before the ALI-794 split, just
|
|
758
843
|
// as two calls instead of one function body.
|
|
759
|
-
async function runLocalSetup(opts
|
|
844
|
+
async function runLocalSetup(opts) {
|
|
760
845
|
const ctx = await runLocalValuePhase(opts);
|
|
761
846
|
await runLocalConnectorPhase(ctx);
|
|
762
847
|
}
|
|
@@ -787,11 +872,18 @@ export async function runSetup(opts = {}) {
|
|
|
787
872
|
// The one place a full brand moment belongs: first run, before any questions.
|
|
788
873
|
printBanner({ version });
|
|
789
874
|
p.intro(commandIntro('align setup'));
|
|
875
|
+
// ALI-949: the wizard's setup_started / setup_completed emitter. NOT offered an env
|
|
876
|
+
// here, where the mode is still unknown: a `--local` run on a machine holding a cloud
|
|
877
|
+
// token would report a cloud setup_started for an explicitly offline session (review on
|
|
878
|
+
// #279). Each mode branch offers its own env at its top - runLocalValuePhase and
|
|
879
|
+
// runCloudSetup - and again once consent / login makes a send possible.
|
|
880
|
+
const funnel = createSetupFunnel();
|
|
790
881
|
// ---- Step 0: Cloud (default) vs local (--local) ----
|
|
791
|
-
// Solo defaults to
|
|
792
|
-
//
|
|
793
|
-
//
|
|
794
|
-
//
|
|
882
|
+
// Solo defaults to CLOUD: telemetry, the real cloud relationship classifier, backup.
|
|
883
|
+
// A work email lands you in the tenant already registered for that domain (or creates
|
|
884
|
+
// one, admin if first); a personal email gets a tenant of one, with a web-only invite
|
|
885
|
+
// path into a company tenant later (reuses the personal->org join flow). --local is
|
|
886
|
+
// the opt-in offline escape hatch; --approve runs the cloud path non-interactively.
|
|
795
887
|
//
|
|
796
888
|
// ALI-794: on a genuinely fresh machine (neither mode configured yet), interactively,
|
|
797
889
|
// with neither flag forcing a mode, invert this - build the local graph and show what
|
|
@@ -799,7 +891,7 @@ export async function runSetup(opts = {}) {
|
|
|
799
891
|
// not asked to sit through that again; they keep the question below, same as today.
|
|
800
892
|
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
801
893
|
if (!opts.local && !opts.approve && interactive && isFreshInstall(config)) {
|
|
802
|
-
await runFreshSetup({ config, env, client, envName, opts });
|
|
894
|
+
await runFreshSetup({ config, env, client, envName, opts, funnel });
|
|
803
895
|
return;
|
|
804
896
|
}
|
|
805
897
|
let mode;
|
|
@@ -813,7 +905,7 @@ export async function runSetup(opts = {}) {
|
|
|
813
905
|
const choice = await p.select({
|
|
814
906
|
message: 'How are you using Align?',
|
|
815
907
|
options: [
|
|
816
|
-
{ value: 'cloud', label: 'Cloud (recommended) - your personal decision graph', hint: '
|
|
908
|
+
{ value: 'cloud', label: 'Cloud (recommended) - your personal decision graph', hint: "work email: your company's graph; personal email: just you" },
|
|
817
909
|
{ value: 'local', label: 'Local only - private, offline, no account', hint: 'stays on this machine (--local)' },
|
|
818
910
|
],
|
|
819
911
|
initialValue: 'cloud',
|
|
@@ -825,10 +917,10 @@ export async function runSetup(opts = {}) {
|
|
|
825
917
|
mode = choice;
|
|
826
918
|
}
|
|
827
919
|
if (mode === 'local') {
|
|
828
|
-
await runLocalSetup({ approve: opts.approve });
|
|
920
|
+
await runLocalSetup({ approve: opts.approve, funnel });
|
|
829
921
|
return;
|
|
830
922
|
}
|
|
831
|
-
await runCloudSetup({ opts, config, env, client, envName });
|
|
923
|
+
await runCloudSetup({ opts, config, env, client, envName, funnel });
|
|
832
924
|
}
|
|
833
925
|
/**
|
|
834
926
|
* ALI-794: the value-first fresh-install flow. Builds and shows the local graph with
|
|
@@ -841,22 +933,24 @@ export async function runSetup(opts = {}) {
|
|
|
841
933
|
* provisional state (component 1's "nothing about the cloud path changes once chosen").
|
|
842
934
|
*/
|
|
843
935
|
async function runFreshSetup(ctx) {
|
|
844
|
-
const phase = await runLocalValuePhase({ approve: ctx.opts.approve });
|
|
936
|
+
const phase = await runLocalValuePhase({ approve: ctx.opts.approve, funnel: ctx.funnel });
|
|
845
937
|
// The value phase's capture report is printed by runLocalConnectorPhase, so choosing
|
|
846
938
|
// cloud below drops it - deliberately: runCloudSetup re-imports git and docs into the
|
|
847
939
|
// cloud tenant and prints its own report, which is the one that describes that graph.
|
|
848
940
|
// Framed by what the graph is missing, not "how are you using Align" in the abstract -
|
|
849
941
|
// there is already a local graph on screen, so the question is whether to extend it.
|
|
850
942
|
//
|
|
851
|
-
// "A path to team sharing", never "for team sharing": choosing cloud
|
|
852
|
-
//
|
|
853
|
-
//
|
|
943
|
+
// "A path to team sharing", never "for team sharing": choosing cloud on a personal
|
|
944
|
+
// email creates a tenant of one (the option's hint says so), and joining a team is a
|
|
945
|
+
// separate web invite later. A work email already lands you in the shared tenant,
|
|
946
|
+
// which is why the hint distinguishes them rather than promising a generic upgrade.
|
|
947
|
+
// The question and the option below must keep agreeing on that - the question
|
|
854
948
|
// overclaimed for a while and a live tester read it as sharing starting here.
|
|
855
949
|
const choice = await p.select({
|
|
856
950
|
message: 'Stay local, or sync to the cloud for backup, richer detection, and a path to team sharing?',
|
|
857
951
|
options: [
|
|
858
952
|
{ value: 'local', label: 'Stay local - keep what you just built, private and offline', hint: 'no account' },
|
|
859
|
-
{ value: 'cloud', label: 'Sync to the cloud - backup, team upgrade path, richer detection', hint: 'personal
|
|
953
|
+
{ value: 'cloud', label: 'Sync to the cloud - backup, team upgrade path, richer detection', hint: "work email: your company's graph; personal email: just you" },
|
|
860
954
|
],
|
|
861
955
|
initialValue: 'local',
|
|
862
956
|
});
|
|
@@ -874,7 +968,7 @@ async function runFreshSetup(ctx) {
|
|
|
874
968
|
// then offer personal-scoped connectors. A personal-email login lands on an
|
|
875
969
|
// isolated personal tenant server-side; connectors auto-bind to it.
|
|
876
970
|
async function runCloudSetup(ctx) {
|
|
877
|
-
const { opts, config, env, envName } = ctx;
|
|
971
|
+
const { opts, config, env, envName, funnel } = ctx;
|
|
878
972
|
let client = ctx.client;
|
|
879
973
|
// ---- Step 1: Auth check (inline login when interactive + unauthenticated) ----
|
|
880
974
|
const authSpinner = p.spinner();
|
|
@@ -882,6 +976,12 @@ async function runCloudSetup(ctx) {
|
|
|
882
976
|
try {
|
|
883
977
|
const me = await client.whoami();
|
|
884
978
|
authSpinner.stop(`Logged in as ${me.user.email} (${me.tenant?.name ?? envName})`);
|
|
979
|
+
// ALI-949: cloud setup has begun, and the token is proven good. Offered only AFTER
|
|
980
|
+
// whoami (Copilot on #279): a stale stored token passes the emitter's token check, so an
|
|
981
|
+
// earlier offer would send a request the gateway rejects, count as sent, and suppress the
|
|
982
|
+
// post-login offer below that would have landed. Reached from every route into cloud
|
|
983
|
+
// setup, including the fresh-install upgrade question.
|
|
984
|
+
void funnel.started(config.getEnvironment(envName));
|
|
885
985
|
}
|
|
886
986
|
catch {
|
|
887
987
|
authSpinner.stop('Not authenticated');
|
|
@@ -899,12 +999,15 @@ async function runCloudSetup(ctx) {
|
|
|
899
999
|
}
|
|
900
1000
|
// Re-create the client so it carries the freshly stored token.
|
|
901
1001
|
client = createGatewayClient(config.getEnvironment(envName));
|
|
1002
|
+
// ALI-949: the first checkpoint at which a fresh cloud install's setup_started CAN
|
|
1003
|
+
// send - the token exists now. `env` above is the pre-login snapshot, so re-read.
|
|
1004
|
+
void funnel.started(config.getEnvironment(envName));
|
|
902
1005
|
}
|
|
903
1006
|
else {
|
|
904
1007
|
// Declined cloud login: offer the local escape hatch instead of failing.
|
|
905
1008
|
const wantLocal = await p.confirm({ message: 'Set up local-only mode instead? (no account, stays on this machine)' });
|
|
906
1009
|
if (!p.isCancel(wantLocal) && wantLocal) {
|
|
907
|
-
await runLocalSetup({ approve: opts.approve });
|
|
1010
|
+
await runLocalSetup({ approve: opts.approve, funnel });
|
|
908
1011
|
return;
|
|
909
1012
|
}
|
|
910
1013
|
p.log.warn(`Run ${chalk.bold('align login')} when ready, then ${chalk.bold('align setup')}.`);
|
|
@@ -1017,9 +1120,11 @@ async function runCloudSetup(ctx) {
|
|
|
1017
1120
|
// `align mcp --remove` as the undo. This block used to write to a user-level config without
|
|
1018
1121
|
// a word when exactly one editor was detected and prompt only at two or more, and that
|
|
1019
1122
|
// multiselect was unguarded, so `align setup --approve` with two agents installed hung.
|
|
1020
|
-
const
|
|
1123
|
+
const globalAgents = await connectDetectedAgents(envName);
|
|
1021
1124
|
// ---- Step 5b: Deterministic auto-alignment files (hook + nudges) ----
|
|
1022
|
-
writeAgentAlignment(envName);
|
|
1125
|
+
const projectAgents = projectAgentsFromWritten(writeAgentAlignment(envName));
|
|
1126
|
+
// ALI-950: project config first - it is the one the user is most likely sitting in.
|
|
1127
|
+
const agents = orderAgents({ project: projectAgents, global: globalAgents.wired });
|
|
1023
1128
|
// ---- Step 6: Git auto-import (zero-auth baseline graph seed) ----
|
|
1024
1129
|
let totalDecisions = 0;
|
|
1025
1130
|
const sourcesImported = [];
|
|
@@ -1200,34 +1305,38 @@ async function runCloudSetup(ctx) {
|
|
|
1200
1305
|
// ---- Outro ----
|
|
1201
1306
|
const decisionsLine = totalDecisions > 0
|
|
1202
1307
|
? ` ${totalDecisions} decisions in your graph`
|
|
1203
|
-
: ` No decisions yet - run ${chalk.bold('align
|
|
1308
|
+
: ` No decisions yet - run ${chalk.bold('align connect')} to load your history`;
|
|
1204
1309
|
const sourceLine = sourcesImported.length > 0
|
|
1205
1310
|
? `\n Sources: ${sourcesImported.join(', ')}`
|
|
1206
1311
|
: '';
|
|
1207
|
-
// ---- Step 8:
|
|
1208
|
-
// Only when something was actually WIRED, not merely detected
|
|
1209
|
-
//
|
|
1210
|
-
//
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
// it. Asked of an AGENT over MCP it is a good opening question, because the agent answers
|
|
1214
|
-
// it by calling a list tool rather than a similarity search. Different surface, different
|
|
1215
|
-
// advice.
|
|
1216
|
-
if (agents.connected > 0) {
|
|
1312
|
+
// ---- Step 8: the agent is connected, and the next step is in it (ALI-950) ----
|
|
1313
|
+
// Only when something was actually WIRED, not merely detected - and the project .mcp.json
|
|
1314
|
+
// counts, which it did not before: this printed only for a global config, so a
|
|
1315
|
+
// Claude-Code-only user never saw it.
|
|
1316
|
+
const connectedLine = agentConnectedLine(agents);
|
|
1317
|
+
if (connectedLine) {
|
|
1217
1318
|
console.log('');
|
|
1218
|
-
p.log.info(chalk.dim(
|
|
1219
|
-
p.log.info(chalk.bold(' "What decisions exist in this codebase?"'));
|
|
1319
|
+
p.log.info(chalk.dim(connectedLine));
|
|
1220
1320
|
}
|
|
1321
|
+
// The question names a decision the wizard just put in the graph, read straight back
|
|
1322
|
+
// rather than trusting the import's tally (same rule as the local found-summary). Only
|
|
1323
|
+
// worth a request when something was imported.
|
|
1324
|
+
const firstFoundTitle = totalDecisions > 0 ? (await firstDecision(client)).firstTitle : undefined;
|
|
1325
|
+
// The last line is the next step, in the agent, with no CLI verb. It used to be
|
|
1326
|
+
// `Run: align ask "any question about your codebase"`.
|
|
1221
1327
|
const outroText = [
|
|
1222
1328
|
chalk.bold('Setup complete.\n'),
|
|
1223
1329
|
decisionsLine,
|
|
1224
1330
|
sourceLine,
|
|
1225
|
-
`\n\n Run: ${chalk.bold('align ask "any question about your codebase"')}`,
|
|
1226
1331
|
chalk.dim('\n\n Want your whole team on a shared decision graph?'),
|
|
1227
1332
|
chalk.dim('\n Upgrade by accepting a team invite - your decisions come with you'),
|
|
1228
1333
|
chalk.dim('\n (you reconnect your connectors once in the team workspace).'),
|
|
1229
1334
|
chalk.dim('\n https://app.align.tech/pricing'),
|
|
1335
|
+
`\n\n ${chalk.bold(agentAskLine({ agents, firstTitle: firstFoundTitle, inRepo: true, envName }))}`,
|
|
1230
1336
|
].join('');
|
|
1231
1337
|
p.outro(outroText);
|
|
1338
|
+
// ALI-949: after the outro, and against the env as it is NOW (the inline login above may
|
|
1339
|
+
// have stored the token after `env` was read). See runLocalConnectorPhase for why awaited.
|
|
1340
|
+
await funnel.completed(config.getEnvironment(envName));
|
|
1232
1341
|
}
|
|
1233
1342
|
//# sourceMappingURL=setup.js.map
|