@aligndottech/cli 0.63.0 → 0.64.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/dist/cli.d.ts +2 -2
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/adjudicate.d.ts +3 -3
- package/dist/commands/capture.js +2 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +30 -30
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/connect-agents.d.ts +4 -2
- package/dist/commands/connect-agents.d.ts.map +1 -1
- package/dist/commands/connect-agents.js +7 -7
- package/dist/commands/connect-agents.js.map +1 -1
- package/dist/commands/connect.d.ts +1 -1
- package/dist/commands/connector/index.js +1 -1
- package/dist/commands/connector/index.js.map +1 -1
- package/dist/commands/context.d.ts +4 -4
- package/dist/commands/context.js +7 -7
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/decisions/index.js +4 -4
- package/dist/commands/decisions/index.js.map +1 -1
- package/dist/commands/default-action.d.ts.map +1 -1
- package/dist/commands/default-action.js +8 -5
- package/dist/commands/default-action.js.map +1 -1
- package/dist/commands/glossary.d.ts +1 -1
- package/dist/commands/import/confluence.js +1 -1
- package/dist/commands/import/confluence.js.map +1 -1
- package/dist/commands/import/git.js +4 -4
- package/dist/commands/import/git.js.map +1 -1
- package/dist/commands/import/jira.js +1 -1
- package/dist/commands/import/jira.js.map +1 -1
- package/dist/commands/import/sessions.d.ts +6 -6
- package/dist/commands/import/sessions.js +10 -10
- package/dist/commands/import/sessions.js.map +1 -1
- package/dist/commands/import.d.ts +2 -2
- package/dist/commands/import.js +2 -2
- package/dist/commands/invite.d.ts +1 -1
- package/dist/commands/invite.js +1 -1
- package/dist/commands/invite.js.map +1 -1
- package/dist/commands/local.js +3 -3
- package/dist/commands/local.js.map +1 -1
- package/dist/commands/mcp.d.ts +4 -4
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +34 -35
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/ratify.d.ts +1 -1
- package/dist/commands/ratify.js +1 -1
- package/dist/commands/ratify.js.map +1 -1
- package/dist/commands/registry.d.ts +2 -2
- package/dist/commands/registry.js +3 -3
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/search.js +2 -2
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/setup.d.ts +14 -10
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +133 -75
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/telemetry.d.ts +2 -2
- package/dist/commands/telemetry.js +3 -3
- package/dist/commands/telemetry.js.map +1 -1
- package/dist/commands/why.js +31 -31
- package/dist/commands/why.js.map +1 -1
- package/dist/detector/local-model-default.d.ts +1 -1
- package/dist/detector/local-model-default.js +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/access-capability.d.ts +1 -1
- package/dist/lib/access-capability.js +1 -1
- package/dist/lib/advisory-verdict.d.ts +3 -3
- package/dist/lib/advisory-verdict.js +3 -3
- package/dist/lib/agent-rules.d.ts +9 -0
- package/dist/lib/agent-rules.d.ts.map +1 -1
- package/dist/lib/agent-rules.js +55 -39
- package/dist/lib/agent-rules.js.map +1 -1
- package/dist/lib/approve-suggestions.d.ts +1 -1
- package/dist/lib/approve-suggestions.js +1 -1
- package/dist/lib/as-of.d.ts +6 -6
- package/dist/lib/as-of.js +6 -6
- package/dist/lib/ask-evidence.js +1 -1
- package/dist/lib/ask-key-offer.js +1 -1
- package/dist/lib/ask-listing.js +1 -1
- package/dist/lib/brand.d.ts +2 -2
- package/dist/lib/brand.js +3 -3
- package/dist/lib/capture-report.d.ts +1 -1
- package/dist/lib/capture-report.d.ts.map +1 -1
- package/dist/lib/capture-report.js +3 -3
- package/dist/lib/capture-report.js.map +1 -1
- package/dist/lib/capture-sources.d.ts +1 -1
- package/dist/lib/capture-sources.js +1 -1
- package/dist/lib/check-depth.d.ts +1 -1
- package/dist/lib/check-depth.js +1 -1
- package/dist/lib/claude-presence.d.ts +1 -1
- package/dist/lib/claude-presence.js +1 -1
- package/dist/lib/cli-oauth.js +1 -1
- package/dist/lib/cli-oauth.js.map +1 -1
- package/dist/lib/commands-doc.d.ts +1 -1
- package/dist/lib/commands-doc.js +1 -1
- package/dist/lib/commit-cite.js +2 -2
- package/dist/lib/commit-shape.d.ts +1 -1
- package/dist/lib/commit-shape.js +1 -1
- package/dist/lib/config.d.ts +6 -6
- package/dist/lib/config.js +7 -7
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/connect-prompt.d.ts +1 -1
- package/dist/lib/connect-prompt.js +1 -1
- package/dist/lib/decider-kind.d.ts +4 -4
- package/dist/lib/decider-kind.js +4 -4
- package/dist/lib/decision-links.d.ts +7 -8
- package/dist/lib/decision-links.d.ts.map +1 -1
- package/dist/lib/decision-links.js +8 -9
- package/dist/lib/decision-links.js.map +1 -1
- package/dist/lib/decision-refs.d.ts +3 -3
- package/dist/lib/decision-refs.js +4 -4
- package/dist/lib/decision-refs.js.map +1 -1
- package/dist/lib/decision-relations.d.ts +4 -4
- package/dist/lib/decision-relations.js +4 -4
- package/dist/lib/decision-render.d.ts +2 -2
- package/dist/lib/decision-render.d.ts.map +1 -1
- package/dist/lib/decision-render.js +5 -5
- package/dist/lib/decision-render.js.map +1 -1
- package/dist/lib/decisions-context.d.ts +4 -4
- package/dist/lib/decisions-context.js +5 -5
- package/dist/lib/decisions-context.js.map +1 -1
- package/dist/lib/doctor-mcp.js +1 -1
- package/dist/lib/doctor.d.ts +2 -2
- package/dist/lib/embedding-assets.js +1 -1
- package/dist/lib/embedding-runtime.js +1 -1
- package/dist/lib/fetchers/capture.d.ts +1 -1
- package/dist/lib/fetchers/docs.d.ts +1 -1
- package/dist/lib/fetchers/docs.js +5 -5
- package/dist/lib/fetchers/docs.js.map +1 -1
- package/dist/lib/fetchers/git.d.ts +1 -1
- package/dist/lib/fetchers/git.js +1 -1
- package/dist/lib/fetchers/github.d.ts +2 -2
- package/dist/lib/fetchers/github.js +2 -2
- package/dist/lib/fetchers/linear.js +1 -1
- package/dist/lib/fetchers/slack.d.ts +2 -2
- package/dist/lib/fetchers/slack.js +2 -2
- package/dist/lib/first-decision.d.ts +1 -1
- package/dist/lib/first-decision.js +1 -1
- package/dist/lib/first-run-pr-import.d.ts +1 -1
- package/dist/lib/format-date.d.ts +1 -1
- package/dist/lib/format-date.js +1 -1
- package/dist/lib/found-summary.js +4 -4
- package/dist/lib/found-summary.js.map +1 -1
- package/dist/lib/gateway-client.d.ts +27 -27
- package/dist/lib/gateway-client.d.ts.map +1 -1
- package/dist/lib/gateway-client.js +30 -30
- package/dist/lib/gateway-client.js.map +1 -1
- package/dist/lib/git.d.ts +10 -10
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +12 -12
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/hook-context.js +1 -1
- package/dist/lib/hook-payload.d.ts +1 -1
- package/dist/lib/hybrid-rank.d.ts +3 -3
- package/dist/lib/hybrid-rank.d.ts.map +1 -1
- package/dist/lib/hybrid-rank.js +3 -3
- package/dist/lib/hybrid-rank.js.map +1 -1
- package/dist/lib/import-defaults.d.ts +5 -5
- package/dist/lib/import-defaults.js +3 -3
- package/dist/lib/inline-rules.d.ts +1 -1
- package/dist/lib/inline-rules.js +1 -1
- package/dist/lib/invite-prompt.d.ts +1 -1
- package/dist/lib/invite-prompt.js +1 -1
- package/dist/lib/launch/mcp-child-env.js +1 -1
- package/dist/lib/launch/mcp-child-env.js.map +1 -1
- package/dist/lib/launch/strict-entry.js +2 -2
- package/dist/lib/launch/strict-entry.js.map +1 -1
- package/dist/lib/lexical-store.d.ts +1 -1
- package/dist/lib/lexical-store.js +1 -1
- package/dist/lib/lexical-terms.d.ts +1 -1
- package/dist/lib/lexical-terms.js +1 -1
- package/dist/lib/lexical-text.d.ts +2 -2
- package/dist/lib/lexical-text.js +2 -2
- package/dist/lib/llm-providers.d.ts +1 -1
- package/dist/lib/llm-providers.js +2 -2
- package/dist/lib/llm-providers.js.map +1 -1
- package/dist/lib/llm-trace.d.ts +1 -1
- package/dist/lib/llm-trace.js +1 -1
- package/dist/lib/llm-trace.js.map +1 -1
- package/dist/lib/local-candidate-classify.d.ts +1 -1
- package/dist/lib/local-candidate-classify.js +3 -3
- package/dist/lib/local-candidate-classify.js.map +1 -1
- package/dist/lib/local-db-migrate.d.ts +6 -6
- package/dist/lib/local-db-migrate.js +9 -9
- package/dist/lib/local-db-migrate.js.map +1 -1
- package/dist/lib/local-db-v11.d.ts +1 -1
- package/dist/lib/local-db.d.ts +33 -33
- package/dist/lib/local-db.d.ts.map +1 -1
- package/dist/lib/local-db.js +27 -27
- package/dist/lib/local-db.js.map +1 -1
- package/dist/lib/local-embeddings-native.js +1 -1
- package/dist/lib/local-embeddings.d.ts +2 -2
- package/dist/lib/local-embeddings.js +2 -2
- package/dist/lib/local-embeddings.js.map +1 -1
- package/dist/lib/local-gateway-client.d.ts +5 -5
- package/dist/lib/local-gateway-client.js +40 -40
- package/dist/lib/local-gateway-client.js.map +1 -1
- package/dist/lib/local-ingest.d.ts +3 -3
- package/dist/lib/local-ingest.js +3 -3
- package/dist/lib/local-link-pass.js +1 -1
- package/dist/lib/local-link-pass.js.map +1 -1
- package/dist/lib/local-llm.d.ts +21 -22
- package/dist/lib/local-llm.d.ts.map +1 -1
- package/dist/lib/local-llm.js +28 -30
- package/dist/lib/local-llm.js.map +1 -1
- package/dist/lib/local-mode.d.ts +1 -1
- package/dist/lib/local-mode.js +2 -2
- package/dist/lib/local-mode.js.map +1 -1
- package/dist/lib/local-provenance.d.ts +1 -1
- package/dist/lib/local-provenance.js +1 -1
- package/dist/lib/local-relationship-classifier.d.ts +6 -6
- package/dist/lib/local-relationship-classifier.js +7 -7
- package/dist/lib/local-relationship-classifier.js.map +1 -1
- package/dist/lib/local-thresholds.d.ts +3 -3
- package/dist/lib/local-thresholds.js +4 -4
- package/dist/lib/local-thresholds.js.map +1 -1
- package/dist/lib/mcp/share-status-tool.js +1 -1
- package/dist/lib/mcp/share-tool.js +1 -1
- package/dist/lib/mcp-setup.d.ts +11 -4
- package/dist/lib/mcp-setup.d.ts.map +1 -1
- package/dist/lib/mcp-setup.js +22 -12
- package/dist/lib/mcp-setup.js.map +1 -1
- package/dist/lib/mcp-timeline-tools.d.ts +5 -5
- package/dist/lib/mcp-timeline-tools.d.ts.map +1 -1
- package/dist/lib/mcp-timeline-tools.js +22 -23
- package/dist/lib/mcp-timeline-tools.js.map +1 -1
- package/dist/lib/next-step.d.ts +11 -5
- package/dist/lib/next-step.d.ts.map +1 -1
- package/dist/lib/next-step.js +12 -4
- package/dist/lib/next-step.js.map +1 -1
- package/dist/lib/personal-import.d.ts +3 -3
- package/dist/lib/personal-import.d.ts.map +1 -1
- package/dist/lib/personal-import.js +4 -4
- package/dist/lib/personal-import.js.map +1 -1
- package/dist/lib/personal-oauth.js +3 -3
- package/dist/lib/personal-oauth.js.map +1 -1
- package/dist/lib/query-expand.d.ts +1 -1
- package/dist/lib/query-expand.js +1 -1
- package/dist/lib/read-value-rollup.d.ts +2 -2
- package/dist/lib/read-value-rollup.js +3 -3
- package/dist/lib/read-value-rollup.js.map +1 -1
- package/dist/lib/reason-extract.d.ts +1 -1
- package/dist/lib/reason-extract.js +1 -1
- package/dist/lib/repo-identity.d.ts +2 -2
- package/dist/lib/repo-identity.js +3 -3
- package/dist/lib/resolve-env.d.ts +1 -1
- package/dist/lib/resolve-env.js +1 -1
- package/dist/lib/scope-defaults.js +1 -1
- package/dist/lib/scope-real.d.ts.map +1 -1
- package/dist/lib/scope-real.js +1 -1
- package/dist/lib/scope-real.js.map +1 -1
- package/dist/lib/search-query.d.ts +2 -2
- package/dist/lib/search-query.js +2 -2
- package/dist/lib/search-rerank.d.ts +1 -1
- package/dist/lib/search-rerank.js +1 -1
- package/dist/lib/sessions/confirm-freetext.d.ts +3 -3
- package/dist/lib/sessions/confirm-freetext.d.ts.map +1 -1
- package/dist/lib/sessions/confirm-freetext.js +2 -2
- package/dist/lib/sessions/extract-freetext.d.ts +3 -3
- package/dist/lib/sessions/extract-freetext.d.ts.map +1 -1
- package/dist/lib/sessions/import-summary.d.ts +1 -1
- package/dist/lib/sessions/import-summary.js +1 -1
- package/dist/lib/sessions/memory-files.d.ts +1 -1
- package/dist/lib/sessions/memory-files.js +5 -5
- package/dist/lib/sessions/source-url.d.ts +2 -2
- package/dist/lib/sessions/source-url.js +2 -2
- package/dist/lib/sessions/types.d.ts +6 -6
- package/dist/lib/sessions/types.js +4 -4
- package/dist/lib/setup-funnel.d.ts +2 -2
- package/dist/lib/setup-ux.d.ts +2 -2
- package/dist/lib/setup-ux.js +3 -3
- package/dist/lib/setup-ux.js.map +1 -1
- package/dist/lib/share/approval.js +1 -1
- package/dist/lib/share/browser-flow.d.ts +1 -1
- package/dist/lib/share/browser-flow.js +1 -1
- package/dist/lib/share/command.js +1 -1
- package/dist/lib/share/envelope.js +1 -1
- package/dist/lib/share/pending.d.ts +1 -1
- package/dist/lib/share/requests-client.d.ts +1 -1
- package/dist/lib/share/run.d.ts +1 -1
- package/dist/lib/sparse-graph.d.ts +20 -0
- package/dist/lib/sparse-graph.d.ts.map +1 -0
- package/dist/lib/sparse-graph.js +39 -0
- package/dist/lib/sparse-graph.js.map +1 -0
- package/dist/lib/suppress-sqlite-warning.d.ts +1 -1
- package/dist/lib/sync/real-env.d.ts.map +1 -1
- package/dist/lib/telemetry-env.d.ts +3 -3
- package/dist/lib/telemetry-env.js +1 -1
- package/dist/lib/token-estimate.d.ts +1 -1
- package/dist/lib/token-estimate.js +3 -3
- package/dist/lib/token-estimate.js.map +1 -1
- package/dist/lib/token-scope-gate.d.ts +1 -1
- package/dist/lib/token-scope-gate.js +1 -1
- package/dist/lib/trusted-align-bin.d.ts +2 -2
- package/dist/lib/trusted-align-bin.d.ts.map +1 -1
- package/dist/lib/trusted-align-bin.js +2 -2
- package/dist/lib/trusted-align-bin.js.map +1 -1
- package/dist/lib/usage-telemetry.d.ts +17 -17
- package/dist/lib/usage-telemetry.js +24 -24
- package/dist/lib/usage-telemetry.js.map +1 -1
- package/dist/lib/user-hooks.d.ts +1 -1
- package/dist/lib/user-hooks.d.ts.map +1 -1
- package/dist/lib/user-hooks.js +3 -3
- package/dist/lib/user-hooks.js.map +1 -1
- package/dist/lib/value-rollup.d.ts +7 -7
- package/dist/lib/value-rollup.d.ts.map +1 -1
- package/dist/lib/value-rollup.js +12 -12
- package/dist/lib/value-rollup.js.map +1 -1
- package/dist/lib/write-confirm.d.ts +53 -0
- package/dist/lib/write-confirm.d.ts.map +1 -0
- package/dist/lib/write-confirm.js +70 -0
- package/dist/lib/write-confirm.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -8,10 +8,13 @@ import { clearScreenForPicker, CLI_TOKEN_SOURCES, cliTokenDecision, detectVerifi
|
|
|
8
8
|
import { createConfigStore } from '../lib/config.js';
|
|
9
9
|
import { createGatewayClient } from '../lib/gateway-client.js';
|
|
10
10
|
import { runPersonalImport, runWithConcurrency } from '../lib/personal-import.js';
|
|
11
|
+
import { detectEditors } from '../lib/mcp-setup.js';
|
|
12
|
+
import { confirmWrites, planSetupWrites } from '../lib/write-confirm.js';
|
|
11
13
|
import { connectDetectedAgents } from './connect-agents.js';
|
|
12
14
|
import { setupAgentAlignment } from '../lib/agent-rules.js';
|
|
13
15
|
import { isGitRepo } from '../lib/git.js';
|
|
14
16
|
import { currentRepoIdentity } from '../lib/repo-identity.js';
|
|
17
|
+
import { sparseGraphHint } from '../lib/sparse-graph.js';
|
|
15
18
|
import { fetchDocsItems } from '../lib/fetchers/docs.js';
|
|
16
19
|
import { createLocalDb } from '../lib/local-db.js';
|
|
17
20
|
import { buildFoundSummary, renderFoundSummary } from '../lib/found-summary.js';
|
|
@@ -34,7 +37,7 @@ import { printBanner } from '../lib/brand.js';
|
|
|
34
37
|
import { guardedPrompt } from '../lib/prompt-guard.js';
|
|
35
38
|
import { setupSummaryLine, unresolvedGaps } from '../lib/connect-prompt.js';
|
|
36
39
|
import { createSetupFunnel } from '../lib/setup-funnel.js';
|
|
37
|
-
import { agentAskLine, agentConnectedLine, firstQuestion, orderAgents, pickAskableTitle, projectAgentsFromWritten } from '../lib/next-step.js';
|
|
40
|
+
import { agentAskLine, agentConnectedLine, agentNotConnectedLine, firstQuestion, orderAgents, pickAskableTitle, projectAgentsFromWritten } from '../lib/next-step.js';
|
|
38
41
|
import { claudeCodeExists } from '../lib/claude-presence.js';
|
|
39
42
|
import { projectForeignNotice } from '../lib/foreign-env.js';
|
|
40
43
|
import { PICK_CANCELLED, pickAgent } from '../lib/launch/pick-agent.js';
|
|
@@ -87,7 +90,7 @@ export function buildSources(gitAvailable) {
|
|
|
87
90
|
fetch: async (t, w = parseSince(undefined), o = {}) => {
|
|
88
91
|
const { fetchGitHubItems, resolveGitHubRepoScope } = await import('../lib/fetchers/github.js');
|
|
89
92
|
// L4: when the caller has resolved the scope (`resolved`), its `repo` is the whole answer and the folder is not consulted.
|
|
90
|
-
// Without it (cloud setup) this is
|
|
93
|
+
// Without it (cloud setup) this is the auto-detect: the repo narrows the search, and team scope only when asked.
|
|
91
94
|
const repo = o.resolved ? o.repo : await resolveGitHubRepoScope({});
|
|
92
95
|
return fetchGitHubItems({ token: t['token'], ...fetchWindow('github', w), ...(repo ? { repo, ...(o.team || o.resolved ? { scope: 'team' } : {}) } : {}) });
|
|
93
96
|
},
|
|
@@ -97,7 +100,7 @@ export function buildSources(gitAvailable) {
|
|
|
97
100
|
description: 'Your issues',
|
|
98
101
|
tier: 'site',
|
|
99
102
|
// Personal/CLI tier is read-only (no write:jira-work). The team/org
|
|
100
|
-
// comment bot keeps write via the `jira` key.
|
|
103
|
+
// comment bot keeps write via the `jira` key.
|
|
101
104
|
oauthKey: 'jira-personal',
|
|
102
105
|
// Local-mode token paste (read-only Atlassian API token + email + site).
|
|
103
106
|
tokenLabel: 'API token',
|
|
@@ -116,7 +119,7 @@ export function buildSources(gitAvailable) {
|
|
|
116
119
|
...CAPTURE_SOURCES.confluence,
|
|
117
120
|
description: 'Your pages and documentation',
|
|
118
121
|
tier: 'site',
|
|
119
|
-
// Read-only personal/CLI tier.
|
|
122
|
+
// Read-only personal/CLI tier.
|
|
120
123
|
oauthKey: 'confluence-personal',
|
|
121
124
|
// Local-mode token paste (read-only Atlassian API token + email + site).
|
|
122
125
|
tokenLabel: 'API token',
|
|
@@ -136,7 +139,7 @@ export function buildSources(gitAvailable) {
|
|
|
136
139
|
description: 'Decision threads from your channels - may need workspace admin [experimental]',
|
|
137
140
|
tier: 'workspace',
|
|
138
141
|
// Read-only personal/CLI tier (no chat:write). The team/org bot keeps
|
|
139
|
-
// chat:write via the `slack` key.
|
|
142
|
+
// chat:write via the `slack` key.
|
|
140
143
|
oauthKey: 'slack-personal',
|
|
141
144
|
// Local-mode token paste: a Slack user token (xoxp-) with read scopes only.
|
|
142
145
|
tokenLabel: 'User token (xoxp-...)',
|
|
@@ -181,14 +184,14 @@ export function buildSources(gitAvailable) {
|
|
|
181
184
|
...CAPTURE_SOURCES.gitlab,
|
|
182
185
|
description: 'Your merge requests',
|
|
183
186
|
tier: 'personal',
|
|
184
|
-
// gitlab.com → read-only browser OAuth (scope read_api
|
|
187
|
+
// gitlab.com → read-only browser OAuth (scope read_api). A
|
|
185
188
|
// self-managed host (custom domain) can't use the fixed gitlab.com OAuth
|
|
186
189
|
// app, so it falls back to the read-only PAT path below.
|
|
187
190
|
oauthKey: 'gitlab-personal',
|
|
188
191
|
hostGatedOAuth: { field: 'domain' },
|
|
189
192
|
tokenLabel: 'Personal access token',
|
|
190
193
|
// Read-only tier: steer users to the read-only scope. `api` would grant
|
|
191
|
-
// write; `read_api` is read-only and all Align's import needs.
|
|
194
|
+
// write; `read_api` is read-only and all Align's import needs.
|
|
192
195
|
// read_api arrives pre-selected via GitLab's documented ?name=&scopes= pre-fill,
|
|
193
196
|
// which works on self-managed hosts too - the docs example is gitlab.example.com.
|
|
194
197
|
tokenHint: 'read_api is pre-selected (read-only). Click Create, then copy the token here',
|
|
@@ -209,7 +212,7 @@ export function buildSources(gitAvailable) {
|
|
|
209
212
|
description: 'Your issues and project discussions',
|
|
210
213
|
tier: 'personal',
|
|
211
214
|
// Read-only personal/CLI tier via browser OAuth (scope `read`), replacing the
|
|
212
|
-
// full-access API-key paste. Requires the Linear OAuth app + sealed creds.
|
|
215
|
+
// full-access API-key paste. Requires the Linear OAuth app + sealed creds.
|
|
213
216
|
oauthKey: 'linear-personal',
|
|
214
217
|
// Local-mode token paste: a Linear personal API key (read-only graph).
|
|
215
218
|
tokenLabel: 'Personal API key (lin_api_...)',
|
|
@@ -231,12 +234,12 @@ export function buildSources(gitAvailable) {
|
|
|
231
234
|
// Read-only personal/CLI tier via browser OAuth (public integration),
|
|
232
235
|
// replacing the internal-integration-secret paste in cloud. Read-only is
|
|
233
236
|
// governed by the integration's capabilities (Read content), not scopes.
|
|
234
|
-
// Requires the Notion OAuth app + sealed creds.
|
|
237
|
+
// Requires the Notion OAuth app + sealed creds.
|
|
235
238
|
oauthKey: 'notion-personal',
|
|
236
239
|
// Local-mode token paste: a read-only internal integration secret.
|
|
237
240
|
tokenLabel: 'Integration secret (ntn_...)',
|
|
238
241
|
// Read-only tier: Align only reads. Notion integration capabilities are set
|
|
239
|
-
// at creation - keep it to "Read content" (no insert/update).
|
|
242
|
+
// at creation - keep it to "Read content" (no insert/update).
|
|
240
243
|
tokenHint: 'Create an integration with ONLY "Read content" capability (no insert/update), then copy its Internal Integration Secret',
|
|
241
244
|
// The developer console's tokens page directly, not the legacy my-integrations
|
|
242
245
|
// landing (both resolve; this one is where the secret actually lives - Tom,
|
|
@@ -272,7 +275,7 @@ async function collectTokens(source, seed = {}, opts = {}) {
|
|
|
272
275
|
}
|
|
273
276
|
// Main token. A seeded token skips the whole block - no browser open, no paste.
|
|
274
277
|
if (source.tokenLabel && tokens['token'] === undefined) {
|
|
275
|
-
// No OAuth here, by design (Tom, 2026-08-31, superseding
|
|
278
|
+
// No OAuth here, by design (Tom, 2026-08-31, superseding the local
|
|
276
279
|
// direction): local mode is the user's personal graph, so the credential is one
|
|
277
280
|
// they mint, scope and revoke themselves. The device-flow/PKCE machinery this
|
|
278
281
|
// block used to try first is deleted - recover it from the history of PR #196 if
|
|
@@ -285,7 +288,7 @@ async function collectTokens(source, seed = {}, opts = {}) {
|
|
|
285
288
|
let refusal;
|
|
286
289
|
if (cliSource) {
|
|
287
290
|
// Detection verifies read-only-ness BEFORE the decision layer, so --approve can
|
|
288
|
-
// never auto-accept a token that was not positively confirmed
|
|
291
|
+
// never auto-accept a token that was not positively confirmed. A
|
|
289
292
|
// refusal is said out loud: a silent skip here reads as "gh not installed" when
|
|
290
293
|
// the truth is "gh is installed and its token can write".
|
|
291
294
|
const detected = await detectVerifiedCliToken(cliSource);
|
|
@@ -339,14 +342,14 @@ async function collectTokens(source, seed = {}, opts = {}) {
|
|
|
339
342
|
return tokens;
|
|
340
343
|
}
|
|
341
344
|
// ---------------------------------------------------------------------------
|
|
342
|
-
// Deterministic auto-alignment
|
|
345
|
+
// Deterministic auto-alignment
|
|
343
346
|
// ---------------------------------------------------------------------------
|
|
344
347
|
// Write the project-local, committed agent-rules files (Claude Code PostToolUse hook +
|
|
345
348
|
// CLAUDE.md nudge + Cursor rule) so alignment context fires regardless of model
|
|
346
349
|
// discretion. Best-effort: a write failure (read-only dir, weird CWD) must never abort
|
|
347
350
|
// onboarding, so we warn and continue.
|
|
348
351
|
/**
|
|
349
|
-
* The retry command printed when a connector import fails mid-setup
|
|
352
|
+
* The retry command printed when a connector import fails mid-setup.
|
|
350
353
|
*
|
|
351
354
|
* It must be runnable AS PRINTED by the user this session belongs to. The bare
|
|
352
355
|
* form resolved to the cloud default, so a --local user pasting our own hint
|
|
@@ -356,10 +359,33 @@ async function collectTokens(source, seed = {}, opts = {}) {
|
|
|
356
359
|
export function importRetryHint(sourceId, envName) {
|
|
357
360
|
return envName === 'prod' ? `align connect ${sourceId}` : `align connect ${sourceId} --env ${envName}`;
|
|
358
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Before setup writes anything: show the exact files it will create or modify, then ask
|
|
364
|
+
* (default yes). Covers the project's agent files and each detected agent's own config, the
|
|
365
|
+
* two writers below. Non-TTY or --approve prints the list and goes ahead. False means the
|
|
366
|
+
* person declined: the caller writes nothing.
|
|
367
|
+
*/
|
|
368
|
+
async function confirmAgentWrites(o) {
|
|
369
|
+
// Detected once: the same agents are listed here and written by connectDetectedAgents.
|
|
370
|
+
const editors = detectEditors();
|
|
371
|
+
const files = planSetupWrites({ cwd: process.cwd(), editors });
|
|
372
|
+
console.log('');
|
|
373
|
+
const confirmed = await confirmWrites({
|
|
374
|
+
files,
|
|
375
|
+
approve: o.approve,
|
|
376
|
+
interactive: o.interactive,
|
|
377
|
+
print: (text) => p.log.info(text),
|
|
378
|
+
ask: async (message) => {
|
|
379
|
+
const answer = await p.confirm({ message, initialValue: true });
|
|
380
|
+
return p.isCancel(answer) ? undefined : answer;
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
return { confirmed, editors };
|
|
384
|
+
}
|
|
359
385
|
/**
|
|
360
386
|
* Returns the repo-relative files written (empty when the write failed), so the outro can
|
|
361
|
-
* tell whether the project .mcp.json - the one that wires Claude Code - actually landed
|
|
362
|
-
*
|
|
387
|
+
* tell whether the project .mcp.json - the one that wires Claude Code - actually landed.
|
|
388
|
+
* Naming an agent as connected off a write that threw would be false.
|
|
363
389
|
*/
|
|
364
390
|
function writeAgentAlignment(envName) {
|
|
365
391
|
try {
|
|
@@ -382,7 +408,7 @@ function writeAgentAlignment(envName) {
|
|
|
382
408
|
// Saved AI provider keys
|
|
383
409
|
// ---------------------------------------------------------------------------
|
|
384
410
|
/**
|
|
385
|
-
* The wizard asks for no AI-provider key. It used to end on
|
|
411
|
+
* The wizard asks for no AI-provider key. It used to end on the guided Groq + Gemini
|
|
386
412
|
* offer, immediately before bare `align` opened the coding agent - and read in that position
|
|
387
413
|
* the keys looked like something the agent needed (a founder's own 0.48.0 run). They only
|
|
388
414
|
* serve align's own terminal `align ask` prose; inside a wrapped agent the agent writes the
|
|
@@ -432,13 +458,13 @@ function nextStepLine(agent, launching, agents) {
|
|
|
432
458
|
const label = agent ? agentByName(agent)?.label : undefined;
|
|
433
459
|
if (label)
|
|
434
460
|
return launching ? null : `Run ${chalk.bold('align')} to open ${label} with your graph.`;
|
|
435
|
-
//
|
|
461
|
+
// Said once, and only when it is true. With a configured agent the ask line below names it.
|
|
436
462
|
if (agents.length > 0)
|
|
437
463
|
return null;
|
|
438
464
|
return `No coding agent was found, so none is connected. Install one from the list above, then run ${chalk.bold('align')}.`;
|
|
439
465
|
}
|
|
440
466
|
/**
|
|
441
|
-
*
|
|
467
|
+
* The project `.mcp.json` is written whether or not anyone can read it, so it only
|
|
442
468
|
* counts as a connected agent when Claude Code (the agent that reads it) exists on this machine.
|
|
443
469
|
*/
|
|
444
470
|
function connectedProjectAgents(written) {
|
|
@@ -469,11 +495,11 @@ async function runLocalValuePhase(opts) {
|
|
|
469
495
|
const localEnv = config.getEnvironment('local');
|
|
470
496
|
const localClient = createGatewayClient(localEnv);
|
|
471
497
|
const capture = createCaptureCollector();
|
|
472
|
-
//
|
|
498
|
+
// Local setup has begun. Sends now on a re-run (consent already on disk); on a
|
|
473
499
|
// first run it cannot, and the post-consent offer below is the one that lands. Fire-and-
|
|
474
500
|
// forget: a slow gateway never delays the value moment.
|
|
475
501
|
void opts.funnel.started(localEnv);
|
|
476
|
-
//
|
|
502
|
+
// Value before questions. Git needs no credential and no consent, so it runs
|
|
477
503
|
// before anything is asked - the found-decisions summary below is what "here is what I
|
|
478
504
|
// found" means, and it has to exist before the agent-wiring/consent/connector steps that
|
|
479
505
|
// used to come first for no reason other than that is the order they were written in.
|
|
@@ -511,7 +537,7 @@ async function runLocalValuePhase(opts) {
|
|
|
511
537
|
if (inGitRepo && !repoKnown) {
|
|
512
538
|
console.log('');
|
|
513
539
|
p.log.info(chalk.dim('First import downloads a local embedding model (~23MB, huggingface.co) and, on npm installs, its runtime (~20MB, github.com), one time.'));
|
|
514
|
-
//
|
|
540
|
+
// The reason behind a change usually lives in its PR, so a GitHub origin's merged PR
|
|
515
541
|
// descriptions are read WHILE git is scanned. Public, bounded, never a prompt, never fails first run.
|
|
516
542
|
const prRead = fetchFirstRunPrs(repo);
|
|
517
543
|
const gitSpinner = p.spinner();
|
|
@@ -561,7 +587,7 @@ async function runLocalValuePhase(opts) {
|
|
|
561
587
|
if (prs.note)
|
|
562
588
|
p.log.info(chalk.dim(prs.note));
|
|
563
589
|
if (items.length || prs.items.length) {
|
|
564
|
-
// The payoff
|
|
590
|
+
// The payoff: name real decisions instead of a bare count, so a
|
|
565
591
|
// first-run user can check the summary against their own repo. Read straight back
|
|
566
592
|
// from the db rather than trusting the import's own tally - see buildFoundSummary's
|
|
567
593
|
// comment on why total/linked come from a COUNT, not from `items.length`.
|
|
@@ -591,14 +617,14 @@ async function runLocalValuePhase(opts) {
|
|
|
591
617
|
}
|
|
592
618
|
}
|
|
593
619
|
// Deterministic auto-alignment files target the local graph (advisory check runs --env local).
|
|
594
|
-
// Deferred until AFTER the value moment above
|
|
620
|
+
// Deferred until AFTER the value moment above: writing project files
|
|
595
621
|
// and asking about telemetry before the user has seen anything real is the footprint-before-
|
|
596
622
|
// value ordering this ticket exists to invert.
|
|
597
|
-
//
|
|
623
|
+
// ADRs + the user's own CLAUDE.md/AGENTS.md content, same zero-auth tier as
|
|
598
624
|
// git above and independent of it - a repo can carry decision-shaped docs with no git
|
|
599
625
|
// history worth mining, or vice versa. fetchDocsItems degrades gracefully with no git
|
|
600
626
|
// remote (falls back to a stable git:// identifier), so this runs unconditionally, and
|
|
601
|
-
// it belongs in the value phase for the same reason git does
|
|
627
|
+
// it belongs in the value phase for the same reason git does: it is
|
|
602
628
|
// zero-credential value, so it has to show before the connector/consent questions,
|
|
603
629
|
// not after them.
|
|
604
630
|
console.log('');
|
|
@@ -624,7 +650,7 @@ async function runLocalValuePhase(opts) {
|
|
|
624
650
|
local: true,
|
|
625
651
|
funnel: { env: localEnv, source: 'docs' },
|
|
626
652
|
});
|
|
627
|
-
//
|
|
653
|
+
// A repo can carry decision-shaped docs and no git history worth mining, and
|
|
628
654
|
// the outro's question must name a decision the wizard FOUND, whichever source found
|
|
629
655
|
// it. Read back the same way the git summary does, only when git left nothing to name.
|
|
630
656
|
if (!firstFoundTitle) {
|
|
@@ -646,13 +672,18 @@ async function runLocalValuePhase(opts) {
|
|
|
646
672
|
if (!(e instanceof SkipDocs))
|
|
647
673
|
localDocsSpinner.stop(`Docs import skipped - ${e.message}`);
|
|
648
674
|
}
|
|
649
|
-
const
|
|
675
|
+
const { confirmed: writesConfirmed, editors } = await confirmAgentWrites({ interactive: Boolean(interactive), approve: opts.approve });
|
|
676
|
+
if (!writesConfirmed)
|
|
677
|
+
p.log.info('Left your agent config alone. Run align again when you want it connected.');
|
|
678
|
+
const projectAgents = writesConfirmed ? connectedProjectAgents(projectAgentsFromWritten(writeAgentAlignment('local'))) : [];
|
|
650
679
|
// The agents installed on this machine, not just the ones this project configures. Local
|
|
651
680
|
// setup skipped this and cloud did not, which is backwards: local mode is the one whose
|
|
652
681
|
// entire pitch is an agent on your own machine reading a graph that never leaves it.
|
|
653
682
|
console.log('');
|
|
654
|
-
const localAgents =
|
|
655
|
-
|
|
683
|
+
const localAgents = writesConfirmed
|
|
684
|
+
? await connectDetectedAgents('local', { verbose: opts.verbose, editors })
|
|
685
|
+
: { detected: 0, connected: 0, wired: [] };
|
|
686
|
+
// Whenever ANY agent was wired - the project .mcp.json counts. This used to print
|
|
656
687
|
// only when a GLOBAL config was written, so a Claude-Code-only user, wired through the
|
|
657
688
|
// project file a few lines up, never saw it. The question itself moves to the outro's
|
|
658
689
|
// last line, where it is the one thing left to do.
|
|
@@ -662,11 +693,11 @@ async function runLocalValuePhase(opts) {
|
|
|
662
693
|
console.log('');
|
|
663
694
|
p.log.info(chalk.dim(connectedLine));
|
|
664
695
|
}
|
|
665
|
-
//
|
|
696
|
+
// The first checkpoint at which the local env is initialised, so a local-mode
|
|
666
697
|
// setup_started is offered against it here. (C6: there is no consent question any more - the
|
|
667
698
|
// one-time notice in cli.ts's preAction is the disclosure local sends wait on.)
|
|
668
699
|
void opts.funnel.started(localEnv);
|
|
669
|
-
return { interactive, config, localEnv, localClient, dbPath, opts, capture, funnel: opts.funnel, agents, firstFoundTitle, agent };
|
|
700
|
+
return { interactive, config, localEnv, localClient, dbPath, opts, capture, funnel: opts.funnel, agents, firstFoundTitle, agent, writesDeclined: !writesConfirmed };
|
|
670
701
|
}
|
|
671
702
|
/** The ids `align connect --source` accepts: every local paste-token source, in picker order. */
|
|
672
703
|
export function localConnectorIds() {
|
|
@@ -678,7 +709,7 @@ export function localConnectorIds() {
|
|
|
678
709
|
/**
|
|
679
710
|
* The picker-and-import tail of local setup: ask which read-only-token sources to connect,
|
|
680
711
|
* collect every credential up front, then fetch and import each one. Extracted from
|
|
681
|
-
* runLocalConnectorPhase for `align connect
|
|
712
|
+
* runLocalConnectorPhase for `align connect`, which is this same flow without the
|
|
682
713
|
* wizard around it - one picker, one paste path, one save rule, in one place. The body is
|
|
683
714
|
* the wizard's; `preselected`, `seedTokens` and `json` are the only additions, and each is
|
|
684
715
|
* inert when absent.
|
|
@@ -695,7 +726,7 @@ export async function connectLocalSources(o) {
|
|
|
695
726
|
// paragraph has now said three different things; the design statement printed to
|
|
696
727
|
// the user below is the durable version.) Only sources with a tokenLabel are
|
|
697
728
|
// pasteable: Teams pastes a Graph access token, Zoom has no personal token and is
|
|
698
|
-
// excluded.
|
|
729
|
+
// excluded.
|
|
699
730
|
//
|
|
700
731
|
// Asked BEFORE the git scan so every question lands on a clean screen and the rest of
|
|
701
732
|
// setup then runs without stopping. The picker used to sit under a screenful of import
|
|
@@ -716,9 +747,9 @@ export async function connectLocalSources(o) {
|
|
|
716
747
|
// the user was sent to a provider page to mint a token with no explanation, which reads
|
|
717
748
|
// as the tool being clumsy rather than as the privacy trade they chose. The constraint
|
|
718
749
|
// is the provider's, not ours - OAuth needs a client secret, and a secret inside a
|
|
719
|
-
// distributed binary is not a secret.
|
|
750
|
+
// distributed binary is not a secret.
|
|
720
751
|
if (interactive && localConnectors.length > 0 && !quiet) {
|
|
721
|
-
// ONE story, stated as the design it is (Tom, 2026-08-31, superseding
|
|
752
|
+
// ONE story, stated as the design it is (Tom, 2026-08-31, superseding the earlier
|
|
722
753
|
// local-OAuth direction): this is the user's PERSONAL graph, so the credential is
|
|
723
754
|
// one they mint, scope and can revoke themselves. Earlier versions blamed the
|
|
724
755
|
// provider ("their sign-in requires a secret"), then blamed us ("the Align app is
|
|
@@ -728,7 +759,7 @@ export async function connectLocalSources(o) {
|
|
|
728
759
|
` saved on this machine, readable only by you, and only ever used to read.\n` +
|
|
729
760
|
` Remove them any time with \`align local forget\`.`));
|
|
730
761
|
}
|
|
731
|
-
//
|
|
762
|
+
// What a previous run already collected. Until this existed, setup asked for every
|
|
732
763
|
// token on every run - it gathered credentials, spent them on one fetch and never called the
|
|
733
764
|
// store that was sitting there. Read before the picker so the picker can say what is saved.
|
|
734
765
|
const savedTokens = new Map();
|
|
@@ -761,7 +792,7 @@ export async function connectLocalSources(o) {
|
|
|
761
792
|
isCancel: (v) => p.isCancel(v),
|
|
762
793
|
});
|
|
763
794
|
// `interactive` computed once, by the caller - see runLocalValuePhase.
|
|
764
|
-
//
|
|
795
|
+
// The found-summary above sits between this picker and the last clean screen,
|
|
765
796
|
// which is the exact condition that used to corrupt clack's in-place redraw for an
|
|
766
797
|
// outside tester (2026-08-30). Clear first so the picker gets its own canvas.
|
|
767
798
|
const askPicker = interactive && !o.preselected;
|
|
@@ -809,7 +840,7 @@ export async function connectLocalSources(o) {
|
|
|
809
840
|
if (isAtlassian && seed['token'] !== undefined && !quiet) {
|
|
810
841
|
p.log.info(chalk.dim(' Using your Atlassian email, domain and API token from the previous connector.'));
|
|
811
842
|
}
|
|
812
|
-
//
|
|
843
|
+
// `--token` (and any other field a flag supplied) seeds every preselected
|
|
813
844
|
// source the way the Atlassian reuse above does - a seeded field is never asked for.
|
|
814
845
|
if (o.seedTokens)
|
|
815
846
|
seed = { ...seed, ...o.seedTokens };
|
|
@@ -828,7 +859,7 @@ export async function connectLocalSources(o) {
|
|
|
828
859
|
seed = { ...seed, ...saved };
|
|
829
860
|
reused = true;
|
|
830
861
|
}
|
|
831
|
-
//
|
|
862
|
+
// With no terminal there is nothing to paste into. Name the flag rather than
|
|
832
863
|
// letting clack crash on a closed stdin, which reads as the tool breaking (align-cli#118).
|
|
833
864
|
if (!interactive) {
|
|
834
865
|
const missing = (source.extraFields ?? []).map((f) => f.key).filter((k) => seed[k] === undefined);
|
|
@@ -853,7 +884,7 @@ export async function connectLocalSources(o) {
|
|
|
853
884
|
}
|
|
854
885
|
}
|
|
855
886
|
// Git and repo docs already ran above, in runLocalValuePhase, before the mode/connector
|
|
856
|
-
// questions
|
|
887
|
+
// questions - both are zero-credential value and belong in the "here is what I
|
|
857
888
|
// found" moment, not stranded behind the questions this phase exists to ask. This phase
|
|
858
889
|
// is only the automatic import for the paste-token connectors just collected.
|
|
859
890
|
for (const { source, tokens, reused } of localReady) {
|
|
@@ -911,7 +942,7 @@ export async function connectLocalSources(o) {
|
|
|
911
942
|
async function runLocalConnectorPhase(ctx) {
|
|
912
943
|
const { interactive, config, localEnv, localClient, dbPath, opts, capture, funnel, agents, firstFoundTitle, agent } = ctx;
|
|
913
944
|
await connectLocalSources({ interactive, config, localEnv, localClient, capture, approve: opts.approve ?? false });
|
|
914
|
-
//
|
|
945
|
+
// What each source fetched and what it could not reach - once, after every
|
|
915
946
|
// import has printed its own line, so the numbers sit together. A source whose fetch
|
|
916
947
|
// THREW is not in it: its spinner line above already named the error, and a row of
|
|
917
948
|
// zeros beside it would read as "fetched nothing" rather than "could not fetch". Empty
|
|
@@ -925,8 +956,8 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
925
956
|
// most local-favouring read preference is the truth: a no-account user is
|
|
926
957
|
// redirected to local and there is nothing to say; a cloud token (or an
|
|
927
958
|
// exported ALIGN_ENV) keeps bare commands on the cloud graph the user did
|
|
928
|
-
// NOT just build. The default deliberately does not move -
|
|
929
|
-
//
|
|
959
|
+
// NOT just build. The default deliberately does not move - personal cloud stays
|
|
960
|
+
// the default and `align env set` its only writer; this
|
|
930
961
|
// warning exists because Session A (2026-08-25) showed the silence reads
|
|
931
962
|
// as the product ignoring an explicit choice.
|
|
932
963
|
const bareEnv = resolveEnv(undefined, { preferLocalEmbedded: true });
|
|
@@ -945,7 +976,7 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
945
976
|
p.log.warn(`Bare commands (align ask, align connect ...) use the ${bareEnv} cloud graph, not this local one, because ${cause}.\n` +
|
|
946
977
|
`Add ${chalk.bold('--env local')} per command, or ${remedy}.`);
|
|
947
978
|
}
|
|
948
|
-
//
|
|
979
|
+
// The graph names its own gaps - a ref whose platform has no connected
|
|
949
980
|
// source. Read directly off decision_refs (the same query status.ts/local.ts use),
|
|
950
981
|
// never gate-y: at most one line, and only when there is a real gap to name.
|
|
951
982
|
const refsDb = createLocalDb(dbPath);
|
|
@@ -953,7 +984,18 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
953
984
|
const gaps = unresolvedGaps(refsDb.getAllRefs(), isConnected);
|
|
954
985
|
refsDb.close();
|
|
955
986
|
const gapLine = setupSummaryLine(gaps);
|
|
956
|
-
//
|
|
987
|
+
// An empty or thin graph gets a specific next step instead of silence. Read from the db's
|
|
988
|
+
// own COUNT, after every import above has run, so it is the graph the user will actually query.
|
|
989
|
+
const totalDb = createLocalDb(dbPath);
|
|
990
|
+
let totalDecisions;
|
|
991
|
+
try {
|
|
992
|
+
totalDecisions = totalDb.getStats().decisions;
|
|
993
|
+
}
|
|
994
|
+
finally {
|
|
995
|
+
totalDb.close();
|
|
996
|
+
}
|
|
997
|
+
const sparseHint = sparseGraphHint(totalDecisions, { bareReadsLocal: bareEnv === 'local' });
|
|
998
|
+
// The last line is the next step, and it happens in the agent - named, with a
|
|
957
999
|
// question about a decision the wizard just found, and no CLI verb. This used to end on
|
|
958
1000
|
// `align ask "why <a thing you decided>"`, a prompt verb on the one screen whose job is to
|
|
959
1001
|
// send the user into their agent. The gap line, when there is one, sits above it.
|
|
@@ -961,30 +1003,35 @@ async function runLocalConnectorPhase(ctx) {
|
|
|
961
1003
|
// Claude Code itself. When it is about to, the last line says so instead.
|
|
962
1004
|
// Said before the outro, once, only when true: with no model the checks the graph exists for cannot run.
|
|
963
1005
|
await runModelStep({ interactive });
|
|
964
|
-
|
|
1006
|
+
// Declined writes: the agent has no align MCP or hook, so opening it "with your graph" would be false.
|
|
1007
|
+
const launching = !ctx.writesDeclined && willLaunchAgent(agent, Boolean(opts.launchNext));
|
|
965
1008
|
const launchLabel = launching && agent ? agentByName(agent)?.label : undefined;
|
|
966
|
-
const nextStep = nextStepLine(agent, launching, agents);
|
|
967
|
-
const askLine =
|
|
968
|
-
?
|
|
969
|
-
:
|
|
970
|
-
? `
|
|
971
|
-
:
|
|
1009
|
+
const nextStep = ctx.writesDeclined ? null : nextStepLine(agent, launching, agents);
|
|
1010
|
+
const askLine = ctx.writesDeclined
|
|
1011
|
+
? agentNotConnectedLine('local')
|
|
1012
|
+
: launchLabel
|
|
1013
|
+
? `Opening ${launchLabel}. Ask it: ${firstQuestion(firstFoundTitle)}`
|
|
1014
|
+
: agents.length === 0 && agent === null
|
|
1015
|
+
? `Once one is installed, open it in this repo and ask: ${firstQuestion(firstFoundTitle)}`
|
|
1016
|
+
: agentAskLine({ agents, firstTitle: firstFoundTitle, inRepo: true, envName: 'local' });
|
|
972
1017
|
p.outro(`${chalk.green('You are set up in local mode.')}\n` +
|
|
973
1018
|
` Graph: ${chalk.dim(dbPath)}\n` +
|
|
974
1019
|
`${nextStep ? ` ${nextStep}\n` : ''}` +
|
|
975
|
-
`${gapLine ? `\n ${chalk.dim(gapLine)}\n` : ''}
|
|
1020
|
+
`${gapLine ? `\n ${chalk.dim(gapLine)}\n` : ''}` +
|
|
1021
|
+
`${sparseHint ? `\n${sparseHint.map((l) => ` ${l}`).join('\n')}\n` : ''}\n` +
|
|
976
1022
|
` ${chalk.bold(askLine)}`);
|
|
977
|
-
//
|
|
1023
|
+
// After the outro, never before it - telemetry must not delay what the user is
|
|
978
1024
|
// waiting for. Awaited (unlike the other emitters) because this is the wizard's last act
|
|
979
1025
|
// and the one row the funnel was blind to; there is nothing left for a 2s worst case to hold up.
|
|
980
1026
|
await funnel.completed(localEnv);
|
|
981
1027
|
}
|
|
982
1028
|
// Local-embedded onboarding (opt-in via --local): no account, no cloud, no OAuth. Composes
|
|
983
|
-
// the two phases above unchanged - this is exactly what ran before the
|
|
1029
|
+
// the two phases above unchanged - this is exactly what ran before the split, just
|
|
984
1030
|
// as two calls instead of one function body.
|
|
985
1031
|
async function runLocalSetup(opts) {
|
|
986
1032
|
const ctx = await runLocalValuePhase(opts);
|
|
987
1033
|
await runLocalConnectorPhase(ctx);
|
|
1034
|
+
return { writesDeclined: ctx.writesDeclined };
|
|
988
1035
|
}
|
|
989
1036
|
/**
|
|
990
1037
|
* The setup flags, on the bare `align` command (there is no `align setup` any more). Hidden from --help: a
|
|
@@ -1000,7 +1047,7 @@ export function addSetupOptions(cmd) {
|
|
|
1000
1047
|
}
|
|
1001
1048
|
/**
|
|
1002
1049
|
* The onboarding flow, extracted from the `setup` action so `align` with no arguments can run
|
|
1003
|
-
* exactly the same thing
|
|
1050
|
+
* exactly the same thing. A new user's first instinct is to type the tool's name,
|
|
1004
1051
|
* and that printed a twenty-command help wall.
|
|
1005
1052
|
*
|
|
1006
1053
|
* A pure move: the body below is the action's, unchanged apart from indentation. Both callers
|
|
@@ -1039,7 +1086,7 @@ export async function runSetup(opts = {}) {
|
|
|
1039
1086
|
printBanner({ version });
|
|
1040
1087
|
p.intro(commandIntro('align'));
|
|
1041
1088
|
await runCloudSetup({ opts, config, env, client: createGatewayClient(env), envName: route.env, funnel: createSetupFunnel() });
|
|
1042
|
-
return;
|
|
1089
|
+
return undefined; // the cloud path never launches an agent
|
|
1043
1090
|
}
|
|
1044
1091
|
// The one place a full brand moment belongs: first run, before any questions.
|
|
1045
1092
|
printBanner({ version });
|
|
@@ -1047,11 +1094,11 @@ export async function runSetup(opts = {}) {
|
|
|
1047
1094
|
if (route.teamLoginUntouched) {
|
|
1048
1095
|
p.log.info(`Your ${route.teamLoginUntouched} team login is untouched. This sets up the local graph only.`);
|
|
1049
1096
|
}
|
|
1050
|
-
//
|
|
1097
|
+
// The wizard's setup_started / setup_completed emitter. NOT offered an env
|
|
1051
1098
|
// here, where the mode is still unknown: each branch offers its own env at its top -
|
|
1052
1099
|
// runLocalValuePhase and runCloudSetup - and again once consent / login makes a send
|
|
1053
1100
|
// possible.
|
|
1054
|
-
await runLocalSetup({ approve: opts.approve, reset: opts.reset, launchNext: opts.launchNext, verbose: opts.verbose, funnel: createSetupFunnel() });
|
|
1101
|
+
return await runLocalSetup({ approve: opts.approve, reset: opts.reset, launchNext: opts.launchNext, verbose: opts.verbose, funnel: createSetupFunnel() });
|
|
1055
1102
|
}
|
|
1056
1103
|
// Cloud (team) onboarding: verify login, wire MCP, seed from git, then offer
|
|
1057
1104
|
// personal-scoped connectors. Reached through routeSetup's team route: an explicit
|
|
@@ -1067,7 +1114,7 @@ async function runCloudSetup(ctx) {
|
|
|
1067
1114
|
try {
|
|
1068
1115
|
const me = await client.whoami();
|
|
1069
1116
|
authSpinner.stop(`Logged in as ${me.user.email} (${me.tenant?.name ?? envName})`);
|
|
1070
|
-
//
|
|
1117
|
+
// Cloud setup has begun, and the token is proven good. Offered only AFTER
|
|
1071
1118
|
// whoami (Copilot on #279): a stale stored token passes the emitter's token check, so an
|
|
1072
1119
|
// earlier offer would send a request the gateway rejects, count as sent, and suppress the
|
|
1073
1120
|
// post-login offer below that would have landed. Reached from every route into cloud
|
|
@@ -1090,7 +1137,7 @@ async function runCloudSetup(ctx) {
|
|
|
1090
1137
|
}
|
|
1091
1138
|
// Re-create the client so it carries the freshly stored token.
|
|
1092
1139
|
client = createGatewayClient(config.getEnvironment(envName));
|
|
1093
|
-
//
|
|
1140
|
+
// The first checkpoint at which a fresh cloud install's setup_started CAN
|
|
1094
1141
|
// send - the token exists now. `env` above is the pre-login snapshot, so re-read.
|
|
1095
1142
|
void funnel.started(config.getEnvironment(envName));
|
|
1096
1143
|
}
|
|
@@ -1203,18 +1250,29 @@ async function runCloudSetup(ctx) {
|
|
|
1203
1250
|
}
|
|
1204
1251
|
// ---- Step 5: MCP editor config (before import - this is the payoff) ----
|
|
1205
1252
|
console.log('');
|
|
1206
|
-
// Shared with the local path
|
|
1253
|
+
// Shared with the local path, which used to skip this entirely - so a local-only
|
|
1207
1254
|
// user got LESS agent wiring than a cloud one, on the mode where an agent running on your
|
|
1208
1255
|
// own machine is the whole point.
|
|
1209
1256
|
//
|
|
1210
|
-
// It
|
|
1211
|
-
//
|
|
1212
|
-
//
|
|
1213
|
-
//
|
|
1214
|
-
|
|
1257
|
+
// It wires every detected agent and discloses each file it touched, with `align mcp --remove`
|
|
1258
|
+
// as the undo. Since the confirmation below it asks first, once, for the whole run: the list
|
|
1259
|
+
// of files, then Continue? (default yes). --approve or no terminal prints the list and goes
|
|
1260
|
+
// on. This block used to write without a word when exactly one editor was detected and
|
|
1261
|
+
// prompt only at two or more, and that multiselect was unguarded, so `align setup --approve`
|
|
1262
|
+
// with two agents installed hung.
|
|
1263
|
+
const { confirmed: writesConfirmed, editors } = await confirmAgentWrites({
|
|
1264
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
1265
|
+
approve: opts.approve,
|
|
1266
|
+
});
|
|
1267
|
+
const writesDeclined = !writesConfirmed;
|
|
1268
|
+
if (!writesConfirmed)
|
|
1269
|
+
p.log.info('Left your agent config alone. Run align again when you want it connected.');
|
|
1270
|
+
const globalAgents = writesConfirmed
|
|
1271
|
+
? await connectDetectedAgents(envName, { verbose: ctx.opts.verbose, editors })
|
|
1272
|
+
: { detected: 0, connected: 0, wired: [] };
|
|
1215
1273
|
// ---- Step 5b: Deterministic auto-alignment files (hook + nudges) ----
|
|
1216
|
-
const projectAgents = connectedProjectAgents(projectAgentsFromWritten(writeAgentAlignment(envName)));
|
|
1217
|
-
//
|
|
1274
|
+
const projectAgents = writesConfirmed ? connectedProjectAgents(projectAgentsFromWritten(writeAgentAlignment(envName))) : [];
|
|
1275
|
+
// Project config first - it is the one the user is most likely sitting in.
|
|
1218
1276
|
const agents = orderAgents({ project: projectAgents, global: globalAgents.wired });
|
|
1219
1277
|
// ---- Step 6: Git auto-import (zero-auth baseline graph seed) ----
|
|
1220
1278
|
let totalDecisions = 0;
|
|
@@ -1369,7 +1427,7 @@ async function runCloudSetup(ctx) {
|
|
|
1369
1427
|
appUrl: resolveAppUrl(env),
|
|
1370
1428
|
quiet: true,
|
|
1371
1429
|
funnel: { env, source: source.id },
|
|
1372
|
-
// Async ingest
|
|
1430
|
+
// Async ingest: return at DB-write speed; titles + links
|
|
1373
1431
|
// enrich in the background. Connection counts show as 0 here and fill in later.
|
|
1374
1432
|
deferEnrichment: true,
|
|
1375
1433
|
});
|
|
@@ -1394,7 +1452,7 @@ async function runCloudSetup(ctx) {
|
|
|
1394
1452
|
console.log(chalk.dim('Query your graph: align ask "..." or align decisions list'));
|
|
1395
1453
|
console.log('');
|
|
1396
1454
|
}
|
|
1397
|
-
//
|
|
1455
|
+
// The capture report, once, after every source has imported.
|
|
1398
1456
|
const captureText = capture.render();
|
|
1399
1457
|
if (captureText) {
|
|
1400
1458
|
console.log(captureText);
|
|
@@ -1407,7 +1465,7 @@ async function runCloudSetup(ctx) {
|
|
|
1407
1465
|
const sourceLine = sourcesImported.length > 0
|
|
1408
1466
|
? `\n Sources: ${sourcesImported.join(', ')}`
|
|
1409
1467
|
: '';
|
|
1410
|
-
// ---- Step 8: the agent is connected, and the next step is in it
|
|
1468
|
+
// ---- Step 8: the agent is connected, and the next step is in it ----
|
|
1411
1469
|
// Only when something was actually WIRED, not merely detected - and the project .mcp.json
|
|
1412
1470
|
// counts, which it did not before: this printed only for a global config, so a
|
|
1413
1471
|
// Claude-Code-only user never saw it.
|
|
@@ -1430,10 +1488,10 @@ async function runCloudSetup(ctx) {
|
|
|
1430
1488
|
chalk.dim('\n Upgrade by accepting a team invite - your decisions come with you'),
|
|
1431
1489
|
chalk.dim('\n (you reconnect your connectors once in the team workspace).'),
|
|
1432
1490
|
chalk.dim('\n https://app.align.tech/pricing'),
|
|
1433
|
-
`\n\n ${chalk.bold(agentAskLine({ agents, firstTitle: firstFoundTitle, inRepo: true, envName }))}`,
|
|
1491
|
+
`\n\n ${chalk.bold(writesDeclined ? agentNotConnectedLine(envName) : agentAskLine({ agents, firstTitle: firstFoundTitle, inRepo: true, envName }))}`,
|
|
1434
1492
|
].join('');
|
|
1435
1493
|
p.outro(outroText);
|
|
1436
|
-
//
|
|
1494
|
+
// After the outro, and against the env as it is NOW (the inline login above may
|
|
1437
1495
|
// have stored the token after `env` was read). See runLocalConnectorPhase for why awaited.
|
|
1438
1496
|
await funnel.completed(config.getEnvironment(envName));
|
|
1439
1497
|
}
|